diff options
Diffstat (limited to 'drawinglayer')
205 files changed, 22000 insertions, 9797 deletions
diff --git a/drawinglayer/CppunitTest_drawinglayer_border.mk b/drawinglayer/CppunitTest_drawinglayer_border.mk index fa2f715590cd..f87321a608c5 100644 --- a/drawinglayer/CppunitTest_drawinglayer_border.mk +++ b/drawinglayer/CppunitTest_drawinglayer_border.mk @@ -21,6 +21,7 @@ $(eval $(call gb_CppunitTest_use_libraries,drawinglayer_border, \ sal \ salhelper \ drawinglayer \ + drawinglayercore \ vcl \ test \ tl \ @@ -30,7 +31,6 @@ $(eval $(call gb_CppunitTest_use_libraries,drawinglayer_border, \ $(eval $(call gb_CppunitTest_use_externals,drawinglayer_border,\ boost_headers \ - libxml2 \ )) $(eval $(call gb_CppunitTest_add_exception_objects,drawinglayer_border, \ diff --git a/drawinglayer/CppunitTest_drawinglayer_processors.mk b/drawinglayer/CppunitTest_drawinglayer_processors.mk new file mode 100644 index 000000000000..627809b814a8 --- /dev/null +++ b/drawinglayer/CppunitTest_drawinglayer_processors.mk @@ -0,0 +1,57 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,drawinglayer_processors)) + +$(eval $(call gb_CppunitTest_use_api,drawinglayer_processors,\ + offapi \ + udkapi \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,drawinglayer_processors, \ + basegfx \ + $(if $(ENABLE_WASM_STRIP_CANVAS),,cppcanvas) \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + drawinglayer \ + drawinglayercore \ + vcl \ + test \ + tl \ + unotest \ + svt \ +)) + +$(eval $(call gb_CppunitTest_use_externals,drawinglayer_processors,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,drawinglayer_processors, \ + drawinglayer/qa/unit/vclmetafileprocessor2d \ + drawinglayer/qa/unit/vclpixelprocessor2d \ +)) + +$(eval $(call gb_CppunitTest_use_ure,drawinglayer_processors)) + +$(eval $(call gb_CppunitTest_use_vcl,drawinglayer_processors)) + +$(eval $(call gb_CppunitTest_use_components,drawinglayer_processors,\ + canvas/source/vcl/vclcanvas \ + canvas/source/factory/canvasfactory \ + configmgr/source/configmgr \ + i18npool/util/i18npool \ + ucb/source/core/ucb1 \ + ucb/source/ucp/file/ucpfile1 \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,drawinglayer_processors)) + +# vim: set noet sw=4 ts=4: diff --git a/drawinglayer/IwyuFilter_drawinglayer.yaml b/drawinglayer/IwyuFilter_drawinglayer.yaml index 5d430b2fd490..c3f3a169cb7b 100644 --- a/drawinglayer/IwyuFilter_drawinglayer.yaml +++ b/drawinglayer/IwyuFilter_drawinglayer.yaml @@ -1,9 +1,6 @@ --- assumeFilename: drawinglayer/source/primitive2d/svggradientprimitive2d.cxx excludelist: - drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx: - # Actually used - - com/sun/star/lang/XMultiServiceFactory.hpp drawinglayer/source/dumper/EnhancedShapeDumper.cxx: # Actually used - com/sun/star/beans/XPropertySet.hpp @@ -13,6 +10,12 @@ excludelist: drawinglayer/source/primitive2d/sceneprimitive2d.cxx: # Needed for direct member access - drawinglayer/attribute/sdrlightattribute3d.hxx + drawinglayer/source/primitive2d/textlayoutdevice.cxx: + # Needed for template + - com/sun/star/uno/XComponentContext.hpp drawinglayer/source/processor2d/vclhelperbufferdevice.cxx: # Needed for direct member access - basegfx/matrix/b2dhommatrix.hxx + drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx: + # Needed for rtl::math::sin + - rtl/math.hxx diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk index 43a42b83b42c..6997bd22457e 100644 --- a/drawinglayer/Library_drawinglayer.mk +++ b/drawinglayer/Library_drawinglayer.mk @@ -20,32 +20,67 @@ $(eval $(call gb_Library_add_defs,drawinglayer,\ $(eval $(call gb_Library_set_precompiled_header,drawinglayer,drawinglayer/inc/pch/precompiled_drawinglayer)) -$(eval $(call gb_Library_set_componentfile,drawinglayer,drawinglayer/drawinglayer)) +$(eval $(call gb_Library_set_componentfile,drawinglayer,drawinglayer/drawinglayer,services)) $(eval $(call gb_Library_use_sdk_api,drawinglayer)) $(eval $(call gb_Library_use_externals,drawinglayer,\ boost_headers \ libxml2 \ + $(if $(USE_HEADLESS_CODE), cairo) \ )) +ifneq ($(ENABLE_WASM_STRIP_CANVAS),TRUE) $(eval $(call gb_Library_use_libraries,drawinglayer,\ - basegfx \ canvastools \ - comphelper \ cppcanvas \ +)) +endif + +$(eval $(call gb_Library_use_system_win32_libs,drawinglayer,\ + d2d1 \ + dxguid \ +)) + +$(eval $(call gb_Library_use_libraries,drawinglayer,\ + basegfx \ + comphelper \ cppu \ cppuhelper \ + drawinglayercore \ i18nlangtag \ sal \ salhelper \ svl \ svt \ - tk \ tl \ vcl \ )) +$(eval $(call gb_Library_use_custom_headers,drawinglayer,\ + officecfg/registry \ +)) + +ifeq ($(OS),WNT) +$(eval $(call gb_Library_use_system_win32_libs,drawinglayer,\ + gdi32 \ +)) +endif + +ifeq ($(OS),WNT) +$(eval $(call gb_Library_add_exception_objects,drawinglayer,\ + drawinglayer/source/processor2d/d2dpixelprocessor2d \ + drawinglayer/source/processor2d/SDPRProcessor2dTools \ +)) +endif + +ifeq ($(USE_HEADLESS_CODE),TRUE) +$(eval $(call gb_Library_add_exception_objects,drawinglayer,\ + drawinglayer/source/processor2d/cairopixelprocessor2d \ + drawinglayer/source/processor2d/SDPRProcessor2dTools \ +)) +endif + $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/animation/animationtiming \ drawinglayer/source/attribute/fillgraphicattribute \ @@ -59,6 +94,7 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/attribute/sdrfillattribute \ drawinglayer/source/attribute/sdrfillgraphicattribute \ drawinglayer/source/attribute/sdrglowattribute \ + drawinglayer/source/attribute/sdrglowtextattribute \ drawinglayer/source/attribute/sdrlightattribute3d \ drawinglayer/source/attribute/sdrlightingattribute3d \ drawinglayer/source/attribute/sdrlineattribute \ @@ -67,27 +103,27 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/attribute/sdrsceneattribute3d \ drawinglayer/source/attribute/sdrshadowattribute \ drawinglayer/source/attribute/strokeattribute \ - drawinglayer/source/geometry/viewinformation2d \ drawinglayer/source/geometry/viewinformation3d \ drawinglayer/source/primitive2d/animatedprimitive2d \ drawinglayer/source/primitive2d/backgroundcolorprimitive2d \ - drawinglayer/source/primitive2d/baseprimitive2d \ drawinglayer/source/primitive2d/bitmapprimitive2d \ + drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D \ drawinglayer/source/primitive2d/borderlineprimitive2d \ drawinglayer/source/primitive2d/controlprimitive2d \ drawinglayer/source/primitive2d/cropprimitive2d \ drawinglayer/source/primitive2d/discretebitmapprimitive2d \ drawinglayer/source/primitive2d/discreteshadowprimitive2d \ drawinglayer/source/primitive2d/embedded3dprimitive2d \ + drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d \ drawinglayer/source/primitive2d/epsprimitive2d \ drawinglayer/source/primitive2d/fillgraphicprimitive2d \ drawinglayer/source/primitive2d/fillgradientprimitive2d \ drawinglayer/source/primitive2d/fillhatchprimitive2d \ drawinglayer/source/primitive2d/glowprimitive2d \ + drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools \ drawinglayer/source/primitive2d/graphicprimitivehelper2d \ drawinglayer/source/primitive2d/graphicprimitive2d \ drawinglayer/source/primitive2d/gridprimitive2d \ - drawinglayer/source/primitive2d/groupprimitive2d \ drawinglayer/source/primitive2d/helplineprimitive2d \ drawinglayer/source/primitive2d/hiddengeometryprimitive2d \ drawinglayer/source/primitive2d/invertprimitive2d \ @@ -106,12 +142,13 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D \ + drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D \ + drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D \ drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D \ drawinglayer/source/primitive2d/primitivetools2d \ - drawinglayer/source/primitive2d/Primitive2DContainer \ drawinglayer/source/primitive2d/sceneprimitive2d \ drawinglayer/source/primitive2d/sdrdecompositiontools2d \ drawinglayer/source/primitive2d/shadowprimitive2d \ @@ -127,7 +164,6 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive2d/textlineprimitive2d \ drawinglayer/source/primitive2d/textprimitive2d \ drawinglayer/source/primitive2d/textstrikeoutprimitive2d \ - drawinglayer/source/primitive2d/Tools \ drawinglayer/source/primitive2d/transformprimitive2d \ drawinglayer/source/primitive2d/transparenceprimitive2d \ drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d \ @@ -152,15 +188,15 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\ drawinglayer/source/primitive3d/shadowprimitive3d \ drawinglayer/source/primitive3d/textureprimitive3d \ drawinglayer/source/primitive3d/transformprimitive3d \ + drawinglayer/source/primitive3d/Tools \ drawinglayer/source/processor2d/baseprocessor2d \ drawinglayer/source/processor2d/processor2dtools \ drawinglayer/source/processor2d/contourextractor2d \ drawinglayer/source/processor2d/getdigitlanguage \ - drawinglayer/source/processor2d/helperwrongspellrenderer \ drawinglayer/source/processor2d/hittestprocessor2d \ drawinglayer/source/processor2d/linegeometryextractor2d \ drawinglayer/source/processor2d/objectinfoextractor2d \ - drawinglayer/source/processor2d/processorfromoutputdevice \ + drawinglayer/source/processor2d/textextractor2d \ drawinglayer/source/processor2d/textaspolygonextractor2d \ drawinglayer/source/processor2d/vclhelperbufferdevice \ drawinglayer/source/processor2d/vclmetafileprocessor2d \ diff --git a/drawinglayer/Library_drawinglayercore.mk b/drawinglayer/Library_drawinglayercore.mk new file mode 100644 index 000000000000..83d2c0faab6d --- /dev/null +++ b/drawinglayer/Library_drawinglayercore.mk @@ -0,0 +1,57 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,drawinglayercore)) + +$(eval $(call gb_Library_set_include,drawinglayercore,\ + $$(INCLUDE) \ + -I$(SRCDIR)/drawinglayer/inc \ +)) + +$(eval $(call gb_Library_add_defs,drawinglayercore,\ + -DDRAWINGLAYERCORE_DLLIMPLEMENTATION \ +)) + +$(eval $(call gb_Library_set_precompiled_header,drawinglayercore,drawinglayer/inc/pch/precompiled_drawinglayercore)) + +$(eval $(call gb_Library_use_sdk_api,drawinglayercore)) + +$(eval $(call gb_Library_use_externals,drawinglayercore,\ + boost_headers \ +)) + +$(eval $(call gb_Library_use_libraries,drawinglayercore,\ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + i18nlangtag \ + sal \ + salhelper \ + svl \ + tl \ + utl \ +)) + +$(eval $(call gb_Library_use_custom_headers,drawinglayercore,\ + officecfg/registry \ +)) + +$(eval $(call gb_Library_add_exception_objects,drawinglayercore,\ + drawinglayer/source/primitive2d/baseprimitive2d \ + drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D \ + drawinglayer/source/primitive2d/BufferedDecompositionFlusher \ + drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D \ + drawinglayer/source/primitive2d/Primitive2DContainer \ + drawinglayer/source/primitive2d/groupprimitive2d \ + drawinglayer/source/primitive2d/Tools \ + drawinglayer/source/geometry/viewinformation2d \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/drawinglayer/Module_drawinglayer.mk b/drawinglayer/Module_drawinglayer.mk index 6d329e95c60a..5a3799991c6d 100644 --- a/drawinglayer/Module_drawinglayer.mk +++ b/drawinglayer/Module_drawinglayer.mk @@ -10,9 +10,14 @@ $(eval $(call gb_Module_Module,drawinglayer)) $(eval $(call gb_Module_add_targets,drawinglayer,\ + Library_drawinglayercore \ Library_drawinglayer \ )) +$(eval $(call gb_Module_add_check_targets,drawinglayer,\ + CppunitTest_drawinglayer_processors \ +)) + $(eval $(call gb_Module_add_slowcheck_targets,drawinglayer,\ CppunitTest_drawinglayer_border \ )) diff --git a/drawinglayer/README b/drawinglayer/README.md index b530ba6fedac..9eb7057d2a37 100644 --- a/drawinglayer/README +++ b/drawinglayer/README.md @@ -1,6 +1,8 @@ +# Drawing API + Drawing API that can specify what to draw via a kind of display list. -Example of the DrawingLayer use is eg. in svx/source/xoutdev/xtabhtch.cxx:121. +Example of the DrawingLayer use is eg. in `svx/source/xoutdev/xtabhtch.cxx:121`. A stripped down version with extended comments: // Create a hatch primitive (here a rectangle that will be filled with @@ -22,24 +24,21 @@ A stripped down version with extended comments: std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(...)); - if (pProcessor2D) - { - // Fill-in the display list. - drawinglayer::primitive2d::Primitive2DSequence aSequence(2); + // Fill-in the display list. + drawinglayer::primitive2d::Primitive2DSequence aSequence(2); - aSequence[0] = aHatchPrimitive; - aSequence[1] = aBlackRectanglePrimitive; + aSequence[0] = aHatchPrimitive; + aSequence[1] = aBlackRectanglePrimitive; - // Render it to the virtual device. - pProcessor2D->process(aSequence); - pProcessor2D.reset(); - } + // Render it to the virtual device. + pProcessor2D->process(aSequence); + pProcessor2D.reset(); // Obtain the bitmap that was rendered from the virtual device, to re-use // it in the widget. aRetval = aVirtualDevice.GetBitmap(Point(0, 0), aVirtualDevice.GetOutputSizePixel()); -== DrawingLayer glossary == +## DrawingLayer Glossary Primitives - classes that represent what should be drawn. It holds the data what to draw, but does not contain any kind of the rendering. Some of the @@ -49,31 +48,31 @@ primitives. Decomposition - a way how to break down the more complicated primitives into the basic primitives, and represent them via them; this logically makes the -plain Primitive2DSequence display list a hierarchy. -Eg. PolygonMarkerPrimitive2D can be decomposed to 2 hairlines -PolyPolygonHairlinePrimitive2D's, each with different color. +plain `Primitive2DSequence` display list a hierarchy. +Eg. `PolygonMarkerPrimitive2D` can be decomposed to 2 hairlines +`PolyPolygonHairlinePrimitive2D`'s, each with different color. Processor - a class that goes through the hierarchy of the Primitives, and -renders it some way. Various processors, like VclPixelProcessor2D (renders to -the screen), VclMetafileProcessor2D (renders to the VCL metafile, eg. for +renders it some way. Various processors, like `VclPixelProcessor2D` (renders to +the screen), `VclMetafileProcessor2D` (renders to the VCL metafile, eg. for printing), etc. -== How to Implement a new Primitive ("something new to draw") == +## How to Implement a New Primitive ("Something New to Draw") -* Create an ancestor of BasePrimitive2D +* Create an ancestor of `BasePrimitive2D` (or of its ancestor if it fits the purpose better) - * Assign it an ID [in drawinglayer_primitivetypes2d.hxx] + * Assign it an ID [in `drawinglayer_primitivetypes2d.hxx`] * Implement its decomposition - [virtual Primitive2DSequence create2DDecomposition(...)] + [`virtual Primitive2DSequence create2DDecomposition(...)`] * Extend the (various) processor(s) If you need more than relying on just the decomposition -== Where is DrawingLayer used == +## Where is DrawingLayer Used -* SdrObject(s) (rectangles, Circles, predefined shapes etc.) +* `SdrObject`(s) (rectangles, Circles, predefined shapes etc.) * Selections @@ -83,3 +82,20 @@ printing), etc. above) * Custom widgets (like the Header / Footer indicator button) + +## Dumping DrawingLayer Primitives as XML + +For debugging purposes, it is possible to dump the drawinglayer primitives as +an xml file. The drawinglayer xml dump can show possible problems with the +rendering. + +For example, in `emfio/qa/cppunit/emf/EmfImportTest.cxx`, one can write: + + Primitive2DSequence aSequence = parseEmf(u"emfio/qa/cppunit/wmf/data/stockobject.emf"); + drawinglayer::Primitive2dXmlDump dumper; + Primitive2DContainer aContainer(aSequence); + dumper.dump(aContainer, "/tmp/drawyinglayer.xml"); + +Then, after invoking `make CppunitTest_emfio_emf`, `/tmp/drawyinglayer.xml` will +be the dump of the drawinglayer primitives used to draw the emf file in +LibreOffice. The top level tag will be <primitive2D>. diff --git a/drawinglayer/inc/converters.hxx b/drawinglayer/inc/converters.hxx deleted file mode 100644 index 4f585d5d08db..000000000000 --- a/drawinglayer/inc/converters.hxx +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- 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 <drawinglayer/primitive2d/baseprimitive2d.hxx> - -namespace drawinglayer -{ - BitmapEx convertToBitmapEx( - const drawinglayer::primitive2d::Primitive2DContainer& rSeq, - const geometry::ViewInformation2D& rViewInformation2D, - sal_uInt32 nDiscreteWidth, - sal_uInt32 nDiscreteHeight, - sal_uInt32 nMaxQuadratPixels); - -} // end of namespace drawinglayer - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/pch/precompiled_drawinglayer.hxx b/drawinglayer/inc/pch/precompiled_drawinglayer.hxx index b8c4aadb7dab..5acb7c70c2ca 100644 --- a/drawinglayer/inc/pch/precompiled_drawinglayer.hxx +++ b/drawinglayer/inc/pch/precompiled_drawinglayer.hxx @@ -13,26 +13,30 @@ manual changes will be rewritten by the next run of update_pch.sh (which presumably also fixes all possible problems, so it's usually better to use it). - Generated on 2020-07-09 17:06:23 using: + Generated on 2021-12-23 08:55:28 using: ./bin/update_pch drawinglayer drawinglayer --cutoff=4 --exclude:system --exclude:module --exclude:local If after updating build fails, use the following command to locate conflicting headers: ./bin/update_pch_bisect ./drawinglayer/inc/pch/precompiled_drawinglayer.hxx "make drawinglayer.build" --find-conflicts */ +#include <sal/config.h> #if PCH_LEVEL >= 1 #include <algorithm> #include <cassert> +#include <cmath> #include <cstddef> -#include <cstring> +#include <cstdlib> #include <deque> #include <limits.h> #include <limits> #include <memory> #include <new> +#include <optional> #include <ostream> #include <string.h> #include <string_view> +#include <type_traits> #include <utility> #include <vector> #endif // PCH_LEVEL >= 1 @@ -43,20 +47,18 @@ #include <rtl/instance.hxx> #include <rtl/math.hxx> #include <rtl/ref.hxx> -#include <rtl/strbuf.h> #include <rtl/string.h> #include <rtl/string.hxx> #include <rtl/stringconcat.hxx> #include <rtl/stringutils.hxx> #include <rtl/textenc.h> +#include <rtl/ustrbuf.hxx> #include <rtl/ustring.h> #include <rtl/ustring.hxx> -#include <sal/config.h> #include <sal/detail/log.h> #include <sal/log.hxx> #include <sal/types.h> #include <vcl/bitmap.hxx> -#include <vcl/bitmapex.hxx> #include <vcl/canvastools.hxx> #include <vcl/dllapi.h> #include <vcl/graph.hxx> @@ -68,7 +70,6 @@ #if PCH_LEVEL >= 3 #include <basegfx/basegfxdllapi.h> #include <basegfx/color/bcolor.hxx> -#include <basegfx/color/bcolormodifier.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <basegfx/matrix/b3dhommatrix.hxx> @@ -90,7 +91,6 @@ #include <basegfx/range/basicrange.hxx> #include <basegfx/tuple/b2dtuple.hxx> #include <basegfx/tuple/b3dtuple.hxx> -#include <basegfx/utils/canvastools.hxx> #include <basegfx/vector/b2dvector.hxx> #include <basegfx/vector/b2enums.hxx> #include <basegfx/vector/b2ivector.hxx> @@ -102,7 +102,7 @@ #include <com/sun/star/drawing/TextureMode.hpp> #include <com/sun/star/drawing/TextureProjectionMode.hpp> #include <com/sun/star/graphic/XPrimitive3D.hpp> -#include <com/sun/star/util/XAccounting.hpp> +#include <com/sun/star/uno/Sequence.h> #include <comphelper/comphelperdllapi.h> #include <comphelper/processfactory.hxx> #include <comphelper/sequence.hxx> @@ -113,22 +113,28 @@ #include <o3tl/cow_wrapper.hxx> #include <o3tl/strong_int.hxx> #include <o3tl/typed_flags_set.hxx> +#include <o3tl/unit_conversion.hxx> #include <salhelper/simplereferenceobject.hxx> +#include <svtools/optionsdrawinglayer.hxx> #include <toolkit/helper/vclunohelper.hxx> #include <tools/color.hxx> +#include <tools/degree.hxx> #include <tools/fontenum.hxx> #include <tools/gen.hxx> -#include <tools/link.hxx> +#include <tools/long.hxx> #include <tools/stream.hxx> #include <tools/toolsdllapi.h> #endif // PCH_LEVEL >= 3 #if PCH_LEVEL >= 4 #include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <drawinglayer/attribute/fillgraphicattribute.hxx> #include <drawinglayer/attribute/fontattribute.hxx> #include <drawinglayer/attribute/lineattribute.hxx> #include <drawinglayer/attribute/materialattribute3d.hxx> #include <drawinglayer/attribute/sdrallattribute3d.hxx> #include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <drawinglayer/attribute/sdrlightattribute3d.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> #include <drawinglayer/attribute/sdrobjectattribute3d.hxx> @@ -136,17 +142,18 @@ #include <drawinglayer/drawinglayerdllapi.h> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/geometry/viewinformation3d.hxx> -#include <drawinglayer/primitive2d/CommonTypes.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx> #include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <drawinglayer/primitive2d/Primitive2DVisitor.hxx> -#include <drawinglayer/primitive2d/Tools.hxx> #include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> @@ -163,8 +170,8 @@ #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/primitivetools2d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> diff --git a/drawinglayer/inc/pch/precompiled_drawinglayercore.cxx b/drawinglayer/inc/pch/precompiled_drawinglayercore.cxx new file mode 100644 index 000000000000..4a8c23ea8e65 --- /dev/null +++ b/drawinglayer/inc/pch/precompiled_drawinglayercore.cxx @@ -0,0 +1,12 @@ +/* -*- 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 "precompiled_drawinglayercore.hxx" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/pch/precompiled_drawinglayercore.hxx b/drawinglayer/inc/pch/precompiled_drawinglayercore.hxx new file mode 100644 index 000000000000..9912df042380 --- /dev/null +++ b/drawinglayer/inc/pch/precompiled_drawinglayercore.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 has been autogenerated by update_pch.sh. It is possible to edit it + manually (such as when an include file has been moved/renamed/removed). All such + manual changes will be rewritten by the next run of update_pch.sh (which presumably + also fixes all possible problems, so it's usually better to use it). + + Generated on 2021-12-23 08:57:20 using: + ./bin/update_pch drawinglayer drawinglayercore --cutoff=3 --exclude:system --exclude:module --exclude:local + + If after updating build fails, use the following command to locate conflicting headers: + ./bin/update_pch_bisect ./drawinglayer/inc/pch/precompiled_drawinglayercore.hxx "make drawinglayer.build" --find-conflicts +*/ + +#include <sal/config.h> +#if PCH_LEVEL >= 1 +#include <deque> +#include <ostream> +#include <vector> +#endif // PCH_LEVEL >= 1 +#if PCH_LEVEL >= 2 +#include <osl/diagnose.h> +#include <osl/interlck.h> +#include <sal/types.h> +#endif // PCH_LEVEL >= 2 +#if PCH_LEVEL >= 3 +#include <basegfx/basegfxdllapi.h> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/basicrange.hxx> +#include <basegfx/tuple/b2dtuple.hxx> +#include <basegfx/utils/canvastools.hxx> +#include <basegfx/vector/b2dvector.hxx> +#endif // PCH_LEVEL >= 3 +#if PCH_LEVEL >= 4 +#include <drawinglayer/drawinglayerdllapi.h> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/CommonTypes.hxx> +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> +#include <drawinglayer/primitive2d/Primitive2DVisitor.hxx> +#include <drawinglayer/primitive2d/Tools.hxx> +#endif // PCH_LEVEL >= 4 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/cropprimitive2d.hxx b/drawinglayer/inc/primitive2d/cropprimitive2d.hxx index ba427eb6fba6..e1c66cffab27 100644 --- a/drawinglayer/inc/primitive2d/cropprimitive2d.hxx +++ b/drawinglayer/inc/primitive2d/cropprimitive2d.hxx @@ -19,14 +19,11 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> #include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> -namespace drawinglayer -{ - namespace primitive2d +namespace drawinglayer::primitive2d { /** CropPrimitive2D class @@ -67,8 +64,8 @@ namespace drawinglayer public: /// constructor CropPrimitive2D( - const Primitive2DContainer& rChildren, - const basegfx::B2DHomMatrix& rTransformation, + Primitive2DContainer&& aChildren, + basegfx::B2DHomMatrix aTransformation, double fCropLeft, double fCropTop, double fCropRight, @@ -90,7 +87,7 @@ namespace drawinglayer /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/graphicprimitivehelper2d.hxx b/drawinglayer/inc/primitive2d/graphicprimitivehelper2d.hxx deleted file mode 100644 index fb1431152b16..000000000000 --- a/drawinglayer/inc/primitive2d/graphicprimitivehelper2d.hxx +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- 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 <drawinglayer/primitive2d/baseprimitive2d.hxx> -#include <vcl/GraphicObject.hxx> - -class Graphic; - -namespace drawinglayer::primitive2d -{ - /** Helper method with supports decomposing a Graphic with all - possible contents to lower level primitives. - - #i121194# Unified to use this helper for FillGraphicPrimitive2D - and GraphicPrimitive2D at the same time. It is able to handle - Bitmaps (with the sub-categories animated bitmap, and SVG), - and Metafiles. - */ - void create2DDecompositionOfGraphic( - Primitive2DContainer& rContainer, - const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform); - - /** Helper to embed given sequence of primitives to evtl. a stack - of ModifiedColorPrimitive2D's to get all the needed modifications - applied. - */ - Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded( - const Primitive2DContainer& rChildren, - GraphicDrawMode aGraphicDrawMode, - double fLuminance = 0.0, // [-1.0 .. 1.0] - double fContrast = 0.0, // [-1.0 .. 1.0] - double fRed = 0.0, // [-1.0 .. 1.0] - double fGreen = 0.0, // [-1.0 .. 1.0] - double fBlue = 0.0, // [-1.0 .. 1.0] - double fGamma = 1.0, // ]0.0 .. 10.0] - bool bInvert = false); - -} // end of namespace drawinglayer::primitive2d - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/texteffectprimitive2d.hxx b/drawinglayer/inc/primitive2d/texteffectprimitive2d.hxx index a39256453f17..8512f64114af 100644 --- a/drawinglayer/inc/primitive2d/texteffectprimitive2d.hxx +++ b/drawinglayer/inc/primitive2d/texteffectprimitive2d.hxx @@ -19,82 +19,77 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> - -namespace drawinglayer +namespace drawinglayer::primitive2d { - namespace primitive2d - { - /** TextEffectStyle2D definition */ - enum class TextEffectStyle2D - { - ReliefEmbossedDefault, - ReliefEngravedDefault, - ReliefEmbossed, - ReliefEngraved, - Outline - }; - - /** TextEffectPrimitive2D class - - This primitive embeds text primitives (normally, as can be seen can - also be used for any other primitives) which have some TextEffect applied - and create the needed geometry and embedding on decomposition. - */ - class TextEffectPrimitive2D final : public BufferedDecompositionPrimitive2D - { - private: - /// the text (or other) content - Primitive2DContainer maTextContent; - - /// the style to apply, the direction and the rotation center - const basegfx::B2DPoint maRotationCenter; - double mfDirection; - TextEffectStyle2D meTextEffectStyle2D; - - /** the last used object to view transformtion used from getDecomposition - for decide buffering - */ - basegfx::B2DHomMatrix maLastObjectToViewTransformation; - - /// create local decomposition - virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; - - public: - /// constructor - TextEffectPrimitive2D( - const Primitive2DContainer& rTextContent, - const basegfx::B2DPoint& rRotationCenter, - double fDirection, - TextEffectStyle2D eTextEffectStyle2D); - - /// data read access - const Primitive2DContainer& getTextContent() const { return maTextContent; } - const basegfx::B2DPoint& getRotationCenter() const { return maRotationCenter; } - double getDirection() const { return mfDirection; } - TextEffectStyle2D getTextEffectStyle2D() const { return meTextEffectStyle2D; } - - /// compare operator - virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; - - /** own get range implementation to solve more effective. Content is by definition displaced - by a fixed discrete unit, thus the contained geometry needs only once be asked for its - own basegfx::B2DRange - */ - virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; - - /// provide unique ID - virtual sal_uInt32 getPrimitive2DID() const override; - - /// Override standard getDecomposition to be view-dependent here - virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override; - }; - } // end of namespace primitive2d -} // end of namespace drawinglayer - +/** TextEffectStyle2D definition */ +enum class TextEffectStyle2D +{ + ReliefEmbossedDefault, + ReliefEngravedDefault, + ReliefEmbossed, + Outline +}; + +/** TextEffectPrimitive2D class + + This primitive embeds text primitives (normally, as can be seen can + also be used for any other primitives) which have some TextEffect applied + and create the needed geometry and embedding on decomposition. +*/ +class TextEffectPrimitive2D final : public BufferedDecompositionPrimitive2D +{ +private: + /// the text (or other) content + Primitive2DContainer maTextContent; + + /// the style to apply, the direction and the rotation center + const basegfx::B2DPoint maRotationCenter; + double mfDirection; + TextEffectStyle2D meTextEffectStyle2D; + + /** the last used object to view transformtion used from getDecomposition + for decide buffering + */ + basegfx::B2DHomMatrix maLastObjectToViewTransformation; + + /// create local decomposition + virtual Primitive2DReference + create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; + +public: + /// constructor + TextEffectPrimitive2D(Primitive2DContainer&& rTextContent, + const basegfx::B2DPoint& rRotationCenter, double fDirection, + TextEffectStyle2D eTextEffectStyle2D); + + /// data read access + const Primitive2DContainer& getTextContent() const { return maTextContent; } + const basegfx::B2DPoint& getRotationCenter() const { return maRotationCenter; } + double getDirection() const { return mfDirection; } + TextEffectStyle2D getTextEffectStyle2D() const { return meTextEffectStyle2D; } + + /// compare operator + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + /** own get range implementation to solve more effective. Content is by definition displaced + by a fixed discrete unit, thus the contained geometry needs only once be asked for its + own basegfx::B2DRange + */ + virtual basegfx::B2DRange + getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override; + + /// Override standard getDecomposition to be view-dependent here + virtual void + get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const override; +}; + +} // end of namespace primitive2d::drawinglayer /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/textlineprimitive2d.hxx b/drawinglayer/inc/primitive2d/textlineprimitive2d.hxx index 059c69bd7ca3..904ee38e31c6 100644 --- a/drawinglayer/inc/primitive2d/textlineprimitive2d.hxx +++ b/drawinglayer/inc/primitive2d/textlineprimitive2d.hxx @@ -19,17 +19,13 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/color/bcolor.hxx> -namespace drawinglayer -{ - namespace primitive2d +namespace drawinglayer::primitive2d { class TextLinePrimitive2D final : public BufferedDecompositionPrimitive2D { @@ -45,12 +41,12 @@ namespace drawinglayer basegfx::BColor maLineColor; /// local decomposition. - virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; + virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; public: /// constructor TextLinePrimitive2D( - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, double fWidth, double fOffset, double fHeight, @@ -71,8 +67,8 @@ namespace drawinglayer /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/textstrikeoutprimitive2d.hxx b/drawinglayer/inc/primitive2d/textstrikeoutprimitive2d.hxx index 1e1e75f7ced1..ffb8c144e77f 100644 --- a/drawinglayer/inc/primitive2d/textstrikeoutprimitive2d.hxx +++ b/drawinglayer/inc/primitive2d/textstrikeoutprimitive2d.hxx @@ -19,9 +19,7 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/color/bcolor.hxx> @@ -29,9 +27,7 @@ #include <com/sun/star/lang/Locale.hpp> -namespace drawinglayer -{ - namespace primitive2d +namespace drawinglayer::primitive2d { class BaseTextStrikeoutPrimitive2D : public BufferedDecompositionPrimitive2D { @@ -46,7 +42,7 @@ namespace drawinglayer public: /// constructor BaseTextStrikeoutPrimitive2D( - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, double fWidth, const basegfx::BColor& rFontColor); @@ -58,13 +54,11 @@ namespace drawinglayer /// compare operator virtual bool operator==( const BasePrimitive2D& rPrimitive ) const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive2d -namespace drawinglayer -{ - namespace primitive2d +namespace drawinglayer::primitive2d { class TextCharacterStrikeoutPrimitive2D final : public BaseTextStrikeoutPrimitive2D { @@ -74,7 +68,7 @@ namespace drawinglayer css::lang::Locale maLocale; /// local decomposition. - virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; + virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; public: /// constructor @@ -83,8 +77,8 @@ namespace drawinglayer double fWidth, const basegfx::BColor& rFontColor, sal_Unicode aStrikeoutChar, - const attribute::FontAttribute& rFontAttribute, - const css::lang::Locale& rLocale); + attribute::FontAttribute aFontAttribute, + css::lang::Locale aLocale); /// data read access sal_Unicode getStrikeoutChar() const { return maStrikeoutChar; } @@ -97,13 +91,11 @@ namespace drawinglayer /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer +} // end of namespace drawinglayer::primitive2d -namespace drawinglayer -{ - namespace primitive2d + +namespace drawinglayer::primitive2d { class TextGeometryStrikeoutPrimitive2D final : public BaseTextStrikeoutPrimitive2D { @@ -113,7 +105,7 @@ namespace drawinglayer TextStrikeout meTextStrikeout; /// local decomposition. - virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; + virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; public: /// constructor @@ -136,8 +128,8 @@ namespace drawinglayer /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive2d/wallpaperprimitive2d.hxx b/drawinglayer/inc/primitive2d/wallpaperprimitive2d.hxx index a8e4b158f30e..f92f2378a2aa 100644 --- a/drawinglayer/inc/primitive2d/wallpaperprimitive2d.hxx +++ b/drawinglayer/inc/primitive2d/wallpaperprimitive2d.hxx @@ -19,16 +19,12 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive2d/primitivetools2d.hxx> -#include <vcl/bitmapex.hxx> +#include <vcl/bitmap.hxx> #include <vcl/wall.hxx> -namespace drawinglayer -{ - namespace primitive2d +namespace drawinglayer::primitive2d { /** WallpaperBitmapPrimitive2D class @@ -47,22 +43,22 @@ namespace drawinglayer { private: basegfx::B2DRange maObjectRange; - BitmapEx maBitmapEx; + Bitmap maBitmap; WallpaperStyle meWallpaperStyle; /// create local decomposition - virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override; + virtual Primitive2DReference create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override; public: /// constructor WallpaperBitmapPrimitive2D( const basegfx::B2DRange& rObjectRange, - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, WallpaperStyle eWallpaperStyle); /// data read access const basegfx::B2DRange& getLocalObjectRange() const { return maObjectRange; } - const BitmapEx& getBitmapEx() const { return maBitmapEx ; } + const Bitmap& getBitmap() const { return maBitmap; } WallpaperStyle getWallpaperStyle() const { return meWallpaperStyle; } /// compare operator @@ -74,7 +70,7 @@ namespace drawinglayer /// provide unique ID virtual sal_uInt32 getPrimitive2DID() const override; }; - } // end of namespace primitive2d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/hatchtextureprimitive3d.hxx b/drawinglayer/inc/primitive3d/hatchtextureprimitive3d.hxx index 1329bf70647f..a60c4da9d0a8 100644 --- a/drawinglayer/inc/primitive3d/hatchtextureprimitive3d.hxx +++ b/drawinglayer/inc/primitive3d/hatchtextureprimitive3d.hxx @@ -19,15 +19,11 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <primitive3d/textureprimitive3d.hxx> #include <drawinglayer/attribute/fillhatchattribute.hxx> -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** HatchTexturePrimitive3D class @@ -53,7 +49,7 @@ namespace drawinglayer public: /// constructor HatchTexturePrimitive3D( - const attribute::FillHatchAttribute& rHatch, + attribute::FillHatchAttribute aHatch, const Primitive3DContainer& rChildren, const basegfx::B2DVector& rTextureSize, bool bModulate, @@ -71,8 +67,8 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/hiddengeometryprimitive3d.hxx b/drawinglayer/inc/primitive3d/hiddengeometryprimitive3d.hxx index 246abe38a664..45de83380bf5 100644 --- a/drawinglayer/inc/primitive3d/hiddengeometryprimitive3d.hxx +++ b/drawinglayer/inc/primitive3d/hiddengeometryprimitive3d.hxx @@ -19,14 +19,10 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive3d/groupprimitive3d.hxx> -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { // This primitive is used to represent geometry for non-visible objects, // e.g. a 3D cube without fill attributes. To still be able to use @@ -54,8 +50,8 @@ namespace drawinglayer // provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/polygontubeprimitive3d.hxx b/drawinglayer/inc/primitive3d/polygontubeprimitive3d.hxx index e27201ad6a12..38372977e1c4 100644 --- a/drawinglayer/inc/primitive3d/polygontubeprimitive3d.hxx +++ b/drawinglayer/inc/primitive3d/polygontubeprimitive3d.hxx @@ -19,14 +19,10 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive3d/polygonprimitive3d.hxx> -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** PolygonStrokePrimitive3D class @@ -85,8 +81,8 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/sdrdecompositiontools3d.hxx b/drawinglayer/inc/primitive3d/sdrdecompositiontools3d.hxx index 9a4d62cbc24f..6e549289b24c 100644 --- a/drawinglayer/inc/primitive3d/sdrdecompositiontools3d.hxx +++ b/drawinglayer/inc/primitive3d/sdrdecompositiontools3d.hxx @@ -19,8 +19,6 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive3d/baseprimitive3d.hxx> #include <com/sun/star/drawing/TextureProjectionMode.hpp> #include <vector> @@ -43,9 +41,7 @@ namespace drawinglayer::attribute { } -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { // #i98295# basegfx::B3DRange getRangeFrom3DGeometry(::std::vector< basegfx::B3DPolyPolygon >& rFill); @@ -85,8 +81,7 @@ namespace drawinglayer const basegfx::B2DVector& rTextureSize, const attribute::Sdr3DObjectAttribute& aSdr3DObjectAttribute); - } // end of namespace primitive3d -} // end of namespace drawinglayer +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/shadowprimitive3d.hxx b/drawinglayer/inc/primitive3d/shadowprimitive3d.hxx index 60ec61fe7dff..06d4e9f49d77 100644 --- a/drawinglayer/inc/primitive3d/shadowprimitive3d.hxx +++ b/drawinglayer/inc/primitive3d/shadowprimitive3d.hxx @@ -19,16 +19,12 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive3d/groupprimitive3d.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/color/bcolor.hxx> -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** ShadowPrimitive3D class @@ -52,7 +48,7 @@ namespace drawinglayer public: /// constructor ShadowPrimitive3D( - const basegfx::B2DHomMatrix& rShadowTransform, + basegfx::B2DHomMatrix aShadowTransform, const basegfx::BColor& rShadowColor, double fShadowTransparence, bool bShadow3D, @@ -70,8 +66,8 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/primitive3d/textureprimitive3d.hxx b/drawinglayer/inc/primitive3d/textureprimitive3d.hxx index 0dc3ed4d3b19..56586217bdff 100644 --- a/drawinglayer/inc/primitive3d/textureprimitive3d.hxx +++ b/drawinglayer/inc/primitive3d/textureprimitive3d.hxx @@ -19,17 +19,13 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/primitive3d/groupprimitive3d.hxx> #include <drawinglayer/attribute/fillgraphicattribute.hxx> #include <basegfx/vector/b2dvector.hxx> #include <drawinglayer/attribute/fillgradientattribute.hxx> -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** TexturePrimitive3D class @@ -65,13 +61,11 @@ namespace drawinglayer /// compare operator virtual bool operator==(const BasePrimitive3D& rPrimitive) const override; }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** UnifiedTransparenceTexturePrimitive3D class @@ -106,13 +100,11 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer +} // end of namespace drawinglayer::primitive3d -namespace drawinglayer -{ - namespace primitive3d + +namespace drawinglayer::primitive3d { /** GradientTexturePrimitive3D class @@ -129,7 +121,7 @@ namespace drawinglayer public: /// constructor GradientTexturePrimitive3D( - const attribute::FillGradientAttribute& rGradient, + attribute::FillGradientAttribute aGradient, const Primitive3DContainer& rChildren, const basegfx::B2DVector& rTextureSize, bool bModulate, @@ -144,13 +136,11 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer +} // end of namespace drawinglayer::primitive3d -namespace drawinglayer -{ - namespace primitive3d + +namespace drawinglayer::primitive3d { /** BitmapTexturePrimitive3D class @@ -182,13 +172,11 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d -namespace drawinglayer -{ - namespace primitive3d +namespace drawinglayer::primitive3d { /** TransparenceTexturePrimitive3D class @@ -212,8 +200,8 @@ namespace drawinglayer /// provide unique ID DeclPrimitive3DIDBlock() }; - } // end of namespace primitive3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::primitive3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/processor3d/defaultprocessor3d.hxx b/drawinglayer/inc/processor3d/defaultprocessor3d.hxx index f23a826a669e..aace2ef87742 100644 --- a/drawinglayer/inc/processor3d/defaultprocessor3d.hxx +++ b/drawinglayer/inc/processor3d/defaultprocessor3d.hxx @@ -22,7 +22,6 @@ #include <drawinglayer/processor3d/baseprocessor3d.hxx> #include <basegfx/range/b2drange.hxx> #include <basegfx/color/bcolormodifier.hxx> -#include <svtools/optionsdrawinglayer.hxx> // predefines @@ -52,9 +51,7 @@ namespace drawinglayer::texture { } -namespace drawinglayer -{ - namespace processor3d +namespace drawinglayer::processor3d { /** DefaultProcessor3D class @@ -85,9 +82,6 @@ namespace drawinglayer /// the current active transparence texture std::shared_ptr< texture::GeoTexSvx > mpTransparenceGeoTexSvx; - /// SvtOptionsDrawinglayer incarnation to react on diverse settings - const SvtOptionsDrawinglayer maDrawinglayerOpt; - /// counter for entered transparence textures sal_uInt32 mnTransparenceCounter; @@ -135,12 +129,9 @@ namespace drawinglayer bool getModulate() const { return mbModulate; } bool getFilter() const { return mbFilter; } bool getSimpleTextureActive() const { return mbSimpleTextureActive; } - - /// access to Drawinglayer configuration options - const SvtOptionsDrawinglayer& getOptionsDrawinglayer() const { return maDrawinglayerOpt; } }; - } // end of namespace processor3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::processor3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/processor3d/geometry2dextractor.hxx b/drawinglayer/inc/processor3d/geometry2dextractor.hxx index ae3050fb2850..8d78110c8dcf 100644 --- a/drawinglayer/inc/processor3d/geometry2dextractor.hxx +++ b/drawinglayer/inc/processor3d/geometry2dextractor.hxx @@ -19,17 +19,13 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/processor3d/baseprocessor3d.hxx> -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/color/bcolormodifier.hxx> -namespace drawinglayer -{ - namespace processor3d +namespace drawinglayer::processor3d { /** Geometry2DExtractingProcessor class @@ -57,14 +53,14 @@ namespace drawinglayer public: Geometry2DExtractingProcessor( const geometry::ViewInformation3D& rViewInformation, - const basegfx::B2DHomMatrix& rObjectTransformation); + basegfx::B2DHomMatrix aObjectTransformation); // data read access const primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; } const basegfx::B2DHomMatrix& getObjectTransformation() const { return maObjectTransformation; } }; - } // end of namespace processor3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::processor3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/processor3d/shadow3dextractor.hxx b/drawinglayer/inc/processor3d/shadow3dextractor.hxx index 37c1c0f0e6fc..64890b7a630a 100644 --- a/drawinglayer/inc/processor3d/shadow3dextractor.hxx +++ b/drawinglayer/inc/processor3d/shadow3dextractor.hxx @@ -19,10 +19,8 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <drawinglayer/processor3d/baseprocessor3d.hxx> -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <basegfx/matrix/b3dhommatrix.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> @@ -32,9 +30,7 @@ namespace basegfx { class B3DPolyPolygon; } namespace basegfx { class B3DPolygon; } -namespace drawinglayer -{ - namespace processor3d +namespace drawinglayer::processor3d { /** Shadow3DExtractingProcessor class @@ -82,7 +78,7 @@ namespace drawinglayer public: Shadow3DExtractingProcessor( const geometry::ViewInformation3D& rViewInformation, - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, const basegfx::B3DVector& rLightNormal, double fShadowSlant, const basegfx::B3DRange& rContained3DRange); @@ -93,7 +89,7 @@ namespace drawinglayer const basegfx::B2DHomMatrix& getObjectTransformation() const { return maObjectTransformation; } const basegfx::B3DHomMatrix& getWorldToEye() const { return maWorldToEye; } }; - } // end of namespace processor3d -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::processor3d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/processor3d/zbufferprocessor3d.hxx b/drawinglayer/inc/processor3d/zbufferprocessor3d.hxx index 9a0b422ac380..0348c56f1f3c 100644 --- a/drawinglayer/inc/processor3d/zbufferprocessor3d.hxx +++ b/drawinglayer/inc/processor3d/zbufferprocessor3d.hxx @@ -27,20 +27,16 @@ namespace basegfx { class BZPixelRaster; } -namespace drawinglayer { - namespace attribute { +namespace drawinglayer::attribute { class SdrSceneAttribute; class SdrLightingAttribute; - class MaterialAttribute3D; - } } + class ZBufferRasterConverter3D; class RasterPrimitive3D; -namespace drawinglayer -{ - namespace processor3d +namespace drawinglayer::processor3d { /** This 3D renderer derived from DefaultProcessor3D renders all fed primitives to a 2D @@ -64,7 +60,7 @@ namespace drawinglayer /* remembered RasterPrimitive3D's which need to be painted back to front for transparent 3D parts */ - std::unique_ptr<std::vector< RasterPrimitive3D >> mpRasterPrimitive3Ds; + mutable std::vector< RasterPrimitive3D > maRasterPrimitive3Ds; sal_uInt32 mnStartLine; sal_uInt32 mnStopLine; @@ -90,7 +86,7 @@ namespace drawinglayer void finish(); }; - } + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/texture/texture.hxx b/drawinglayer/inc/texture/texture.hxx index 24e23a4b241c..aa6e79515b37 100644 --- a/drawinglayer/inc/texture/texture.hxx +++ b/drawinglayer/inc/texture/texture.hxx @@ -19,8 +19,6 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/range/b2drange.hxx> #include <basegfx/color/bcolor.hxx> @@ -45,28 +43,24 @@ namespace drawinglayer::texture virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const; }; - /// helper class for processing equal number of matrices and colors - /// for texture processing - struct B2DHomMatrixAndBColor - { - basegfx::B2DHomMatrix maB2DHomMatrix; - basegfx::BColor maBColor; - }; - class GeoTexSvxGradient : public GeoTexSvx { protected: basegfx::ODFGradientInfo maGradientInfo; basegfx::B2DRange maDefinitionRange; - basegfx::BColor maStart; - basegfx::BColor maEnd; + sal_uInt32 mnRequestedSteps; + basegfx::BColorStops mnColorStops; double mfBorder; + // provide a single buffer entry used for gradient texture + // mapping, see ::modifyBColor implementations + mutable basegfx::BColorStops::BColorStopRange maLastColorStopRange; + public: GeoTexSvxGradient( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder); virtual ~GeoTexSvxGradient() override; @@ -75,8 +69,7 @@ namespace drawinglayer::texture // virtual base methods virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) = 0; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) = 0; }; class GeoTexSvxGradientLinear final : public GeoTexSvxGradient @@ -89,16 +82,14 @@ namespace drawinglayer::texture GeoTexSvxGradientLinear( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle); virtual ~GeoTexSvxGradientLinear() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; @@ -111,16 +102,14 @@ namespace drawinglayer::texture GeoTexSvxGradientAxial( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle); virtual ~GeoTexSvxGradientAxial() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; @@ -129,17 +118,15 @@ namespace drawinglayer::texture public: GeoTexSvxGradientRadial( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY); virtual ~GeoTexSvxGradientRadial() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; @@ -148,9 +135,8 @@ namespace drawinglayer::texture public: GeoTexSvxGradientElliptical( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, @@ -158,8 +144,7 @@ namespace drawinglayer::texture virtual ~GeoTexSvxGradientElliptical() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; @@ -168,9 +153,8 @@ namespace drawinglayer::texture public: GeoTexSvxGradientSquare( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, @@ -178,8 +162,7 @@ namespace drawinglayer::texture virtual ~GeoTexSvxGradientSquare() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; @@ -188,9 +171,8 @@ namespace drawinglayer::texture public: GeoTexSvxGradientRect( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, @@ -198,8 +180,7 @@ namespace drawinglayer::texture virtual ~GeoTexSvxGradientRect() override; virtual void appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) override; + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; }; diff --git a/drawinglayer/inc/texture/texture3d.hxx b/drawinglayer/inc/texture/texture3d.hxx index 47ef9ff450a1..cf076881a061 100644 --- a/drawinglayer/inc/texture/texture3d.hxx +++ b/drawinglayer/inc/texture/texture3d.hxx @@ -19,18 +19,15 @@ #pragma once -#include <drawinglayer/drawinglayerdllapi.h> - #include <texture/texture.hxx> -#include <vcl/bitmapex.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/BitmapReadAccess.hxx> namespace drawinglayer::primitive3d { class HatchTexturePrimitive3D; } -namespace drawinglayer -{ - namespace texture +namespace drawinglayer::texture { class GeoTexSvxMono final : public GeoTexSvx { @@ -47,48 +44,37 @@ namespace drawinglayer virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const override; }; - } // end of namespace texture -} // end of namespace drawinglayer -namespace drawinglayer -{ - namespace texture +} // end of namespace drawinglayer::texture + +namespace drawinglayer::texture { class GeoTexSvxBitmapEx : public GeoTexSvx { protected: - BitmapEx maBitmapEx; - Bitmap maBitmap; // Bitmap held within maBitmapEx, to exist during mpReadBitmap scope - Bitmap::ScopedReadAccess mpReadBitmap; - Bitmap maTransparence; - Bitmap::ScopedReadAccess mpReadTransparence; + Bitmap maBitmap; + BitmapScopedReadAccess mpReadBitmap; basegfx::B2DPoint maTopLeft; basegfx::B2DVector maSize; double mfMulX; double mfMulY; - bool mbIsAlpha : 1; - bool mbIsTransparent : 1; - // helpers bool impIsValid(const basegfx::B2DPoint& rUV, sal_Int32& rX, sal_Int32& rY) const; - sal_uInt8 impGetTransparence(sal_Int32 rX, sal_Int32 rY) const; public: GeoTexSvxBitmapEx( - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, const basegfx::B2DRange& rRange); virtual ~GeoTexSvxBitmapEx() override; virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const override; }; - } // end of namespace texture -} // end of namespace drawinglayer -namespace drawinglayer -{ - namespace texture +} // end of namespace drawinglayer::texture + +namespace drawinglayer::texture { class GeoTexSvxBitmapExTiled final : public GeoTexSvxBitmapEx { @@ -103,7 +89,7 @@ namespace drawinglayer public: GeoTexSvxBitmapExTiled( - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, const basegfx::B2DRange& rRange, double fOffsetX, double fOffsetY); @@ -111,12 +97,10 @@ namespace drawinglayer virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const override; }; - } // end of namespace texture -} // end of namespace drawinglayer -namespace drawinglayer -{ - namespace texture +} // end of namespace drawinglayer::texture + +namespace drawinglayer::texture { class GeoTexSvxMultiHatch final : public GeoTexSvx { @@ -137,7 +121,7 @@ namespace drawinglayer virtual void modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& rfOpacity) const override; virtual void modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const override; }; - } // end of namespace texture -} // end of namespace drawinglayer + +} // end of namespace drawinglayer::texture /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/inc/wmfemfhelper.hxx b/drawinglayer/inc/wmfemfhelper.hxx index 214d2a5d785c..f085065c1ea6 100644 --- a/drawinglayer/inc/wmfemfhelper.hxx +++ b/drawinglayer/inc/wmfemfhelper.hxx @@ -22,10 +22,10 @@ #include <sal/config.h> #include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <vcl/font.hxx> -#include <vcl/outdevstate.hxx> +#include <rtl/ref.hxx> +#include <vcl/rendercontext/State.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> -#include <memory> // predefines namespace drawinglayer::geometry { class ViewInformation2D; } @@ -43,19 +43,24 @@ namespace wmfemfhelper class TargetHolder { private: - std::vector< std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> > aTargets; + drawinglayer::primitive2d::Primitive2DContainer aTargets; public: TargetHolder(); ~TargetHolder(); sal_uInt32 size() const; - void append(std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> pCandidate); + void append(const rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> & pCandidate) + { + append(pCandidate.get()); + } + void append(drawinglayer::primitive2d::Primitive2DContainer xCandidate) + { + aTargets.append(std::move(xCandidate)); + } + void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate); drawinglayer::primitive2d::Primitive2DContainer getPrimitive2DSequence(const PropertyHolder& rPropertyHolder); }; -} -namespace wmfemfhelper -{ /** Helper class which builds a stack on the TargetHolder class */ class TargetHolders { @@ -70,10 +75,7 @@ namespace wmfemfhelper TargetHolder& Current(); ~TargetHolders(); }; -} -namespace wmfemfhelper -{ /** helper class for graphic context This class allows to hold a complete representation of classic @@ -96,14 +98,14 @@ namespace wmfemfhelper basegfx::BColor maOverlineColor; /// clipping - basegfx::B2DPolyPolygon maClipPolyPoygon; + basegfx::B2DPolyPolygon maClipPolyPolygon; /// font, etc. vcl::Font maFont; RasterOp maRasterOp; - ComplexTextLayoutFlags mnLayoutMode; + vcl::text::ComplexTextLayoutFlags mnLayoutMode; LanguageType maLanguageType; - PushFlags mnPushFlags; + vcl::PushFlags mnPushFlags; /// contains all active markers bool mbLineColor : 1; @@ -154,8 +156,8 @@ namespace wmfemfhelper bool getOverlineColorActive() const { return mbOverlineColor; } void setOverlineColorActive(bool bNew) { if (bNew != mbOverlineColor) mbOverlineColor = bNew; } - const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPoygon; } - void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if (rNew != maClipPolyPoygon) maClipPolyPoygon = rNew; } + const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPolygon; } + void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if (rNew != maClipPolyPolygon) maClipPolyPolygon = rNew; } bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive; } void setClipPolyPolygonActive(bool bNew) { if (bNew != mbClipPolyPolygonActive) mbClipPolyPolygonActive = bNew; } @@ -168,21 +170,18 @@ namespace wmfemfhelper bool isRasterOpForceBlack() const { return RasterOp::N0 == maRasterOp; } bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); } - ComplexTextLayoutFlags getLayoutMode() const { return mnLayoutMode; } - void setLayoutMode(ComplexTextLayoutFlags nNew) { if (nNew != mnLayoutMode) mnLayoutMode = nNew; } + vcl::text::ComplexTextLayoutFlags getLayoutMode() const { return mnLayoutMode; } + void setLayoutMode(vcl::text::ComplexTextLayoutFlags nNew) { if (nNew != mnLayoutMode) mnLayoutMode = nNew; } LanguageType getLanguageType() const { return maLanguageType; } void setLanguageType(LanguageType aNew) { if (aNew != maLanguageType) maLanguageType = aNew; } - PushFlags getPushFlags() const { return mnPushFlags; } - void setPushFlags(PushFlags nNew) { if (nNew != mnPushFlags) mnPushFlags = nNew; } + vcl::PushFlags getPushFlags() const { return mnPushFlags; } + void setPushFlags(vcl::PushFlags nNew) { if (nNew != mnPushFlags) mnPushFlags = nNew; } bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); } }; -} -namespace wmfemfhelper -{ /** stack for properties This class builds a stack based on the PropertyHolder @@ -200,15 +199,12 @@ namespace wmfemfhelper public: PropertyHolders(); void PushDefault(); - void Push(PushFlags nPushFlags); + void Push(vcl::PushFlags nPushFlags); void Pop(); PropertyHolder& Current(); ~PropertyHolders(); }; -} -namespace wmfemfhelper -{ drawinglayer::primitive2d::Primitive2DContainer interpretMetafile( const GDIMetaFile& rMetaFile, const drawinglayer::geometry::ViewInformation2D& rViewInformation); diff --git a/drawinglayer/qa/unit/border.cxx b/drawinglayer/qa/unit/border.cxx index 8c4e6e08419b..682bfeb17281 100644 --- a/drawinglayer/qa/unit/border.cxx +++ b/drawinglayer/qa/unit/border.cxx @@ -13,9 +13,10 @@ #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <drawinglayer/processor2d/baseprocessor2d.hxx> -#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> #include <rtl/ref.hxx> #include <test/bootstrapfixture.hxx> #include <vcl/metaact.hxx> @@ -47,22 +48,23 @@ CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoubleDecompositionSolid) double const fExtendRightEnd = 0; basegfx::BColor aColorRight; basegfx::BColor aColorLeft; - const std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); - const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashing); - std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines; + std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines{ - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine( - drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, - fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd)); + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, + fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd), - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine(fDistance)); + drawinglayer::primitive2d::BorderLine(fDistance), - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine( - drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, - fExtendRightStart, fExtendRightEnd, fExtendRightEnd)); + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, + fExtendRightStart, fExtendRightEnd, fExtendRightEnd) + }; rtl::Reference<drawinglayer::primitive2d::BorderLinePrimitive2D> aBorder( - new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, aBorderlines, + new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, std::move(aBorderlines), aStrokeAttribute)); // Decompose it into polygons. @@ -71,11 +73,14 @@ CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoubleDecompositionSolid) aBorder->get2DDecomposition(aContainer, aView); // Make sure it results in two borders as it's a double one. - CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), aContainer.size()); + CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), aContainer.size()); + auto* pGroupPrimitive + = dynamic_cast<const drawinglayer::primitive2d::GroupPrimitive2D*>(aContainer[0].get()); + CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), pGroupPrimitive->getChildren().size()); // Get the inside line, now a PolygonStrokePrimitive2D auto pInside = dynamic_cast<const drawinglayer::primitive2d::PolygonStrokePrimitive2D*>( - aContainer[0].get()); + pGroupPrimitive->getChildren()[0].get()); CPPUNIT_ASSERT(pInside); // Make sure the inside line's height is fLeftWidth. @@ -88,15 +93,23 @@ CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoubleDecompositionSolid) CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoublePixelProcessing) { - // Create a pixel processor. + // Creating a pixel-processor and after that attaching a metafile + // recording is not possible anymore, the pixel-processor may be + // a SDPR, e.g. a CairoSDPR, and *not* a VclPixelProcessor2D anymore. + // Since the intention had changed already (see comments below + // where it is explained why two lines are expected nowadays) + // it is also okay to just use a VclMetafileProcessor2D - to record + // a metafile. ScopedVclPtrInstance<VirtualDevice> pDev; + GDIMetaFile aMetaFile; + aMetaFile.Record(pDev); drawinglayer::geometry::ViewInformation2D aView; + + // This creates a VclMetafileProcessor2D - the only processor that + // (as the name states) can record metafiles std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( - drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(*pDev, aView)); + drawinglayer::processor2d::createProcessor2DFromOutputDevice(*pDev, aView)); CPPUNIT_ASSERT(pProcessor); - GDIMetaFile aMetaFile; - // Start recording after the processor is created, so we can test the pixel processor. - aMetaFile.Record(pDev); // Create a border line primitive that's similar to the one from the bugdoc: // 1.47 pixels is 0.03cm at 130% zoom and 96 DPI. @@ -111,26 +124,26 @@ CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoublePixelProcessing) double const fExtendRightEnd = 0; basegfx::BColor aColorRight; basegfx::BColor aColorLeft; - const std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); - const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(aDashing); - std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines; - - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine( - drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, - fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd)); + std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines{ + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, + fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd), - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine(fDistance)); + drawinglayer::primitive2d::BorderLine(fDistance), - aBorderlines.push_back(drawinglayer::primitive2d::BorderLine( - drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, - fExtendRightStart, fExtendRightEnd, fExtendRightEnd)); + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, + fExtendRightStart, fExtendRightEnd, fExtendRightEnd) + }; rtl::Reference<drawinglayer::primitive2d::BorderLinePrimitive2D> aBorder( - new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, aBorderlines, + new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, std::move(aBorderlines), aStrokeAttribute)); drawinglayer::primitive2d::Primitive2DContainer aPrimitives; - aPrimitives.push_back(drawinglayer::primitive2d::Primitive2DReference(aBorder.get())); + aPrimitives.push_back(drawinglayer::primitive2d::Primitive2DReference(aBorder)); // Process the primitives. pProcessor->process(aPrimitives); diff --git a/drawinglayer/qa/unit/vclmetafileprocessor2d.cxx b/drawinglayer/qa/unit/vclmetafileprocessor2d.cxx new file mode 100644 index 000000000000..7845e1623693 --- /dev/null +++ b/drawinglayer/qa/unit/vclmetafileprocessor2d.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/. + * + */ + +#include <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <cppcanvas/vclfactory.hxx> + +#include <com/sun/star/rendering/XCanvas.hpp> + +using namespace drawinglayer; +using namespace com::sun::star; + +class VclMetaFileProcessor2DTest : public test::BootstrapFixture +{ + VclPtr<VirtualDevice> mVclDevice; + uno::Reference<rendering::XCanvas> mCanvas; + + // if enabled - check the result images with: + // "xdg-open ./workdir/CppunitTest/drawinglayer_processors.test.core/" + static constexpr const bool mbExportBitmap = false; + + void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device) + { + if (mbExportBitmap) + { + Bitmap aBitmap(device->GetBitmap(Point(0, 0), device->GetOutputSizePixel())); + SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmap, aStream); + } + } + +public: + VclMetaFileProcessor2DTest() + : BootstrapFixture(true, false) + { + } + + virtual void tearDown() override + { + mVclDevice.reset(); + mCanvas = uno::Reference<rendering::XCanvas>(); + BootstrapFixture::tearDown(); + } + + void setupCanvas(const Size& size, Color backgroundColor = COL_WHITE, bool alpha = false) + { + mVclDevice = alpha ? VclPtr<VirtualDevice>::Create(DeviceFormat::WITH_ALPHA) + : VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA); + mVclDevice->SetOutputSizePixel(size); + mVclDevice->SetBackground(Wallpaper(backgroundColor)); + mVclDevice->Erase(); + mCanvas = mVclDevice->GetCanvas(); + CPPUNIT_ASSERT(mCanvas.is()); + } + + // Test drawing a dotted line in Impress presentation mode. + void testTdf136957() + { + // Impress presentation mode first draws the slide to a metafile. + GDIMetaFile metafile; + // I got these values by adding debug output to cppcanvas::internal::ImplRenderer::ImplRenderer(). + metafile.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + metafile.SetPrefSize(Size(14548, 3350)); + ScopedVclPtrInstance<VirtualDevice> metadevice; + metafile.Record(metadevice); + drawinglayer::geometry::ViewInformation2D view; + std::unique_ptr<processor2d::BaseProcessor2D> processor( + processor2d::createProcessor2DFromOutputDevice(*metadevice, view)); + CPPUNIT_ASSERT(processor); + // Match the values Impress uses. + basegfx::B2DPolygon polygon = { { 15601, 0 }, { 15602, 5832 } }; + attribute::LineAttribute lineAttributes( + basegfx::BColor(0.047058823529411764, 0.19607843137254902, 0.17254901960784313), 35, + basegfx::B2DLineJoin::Miter, css::drawing::LineCap_ROUND); + attribute::StrokeAttribute strokeAttributes({ 0.35, 69.65 }); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> strokePrimitive( + new primitive2d::PolygonStrokePrimitive2D(polygon, lineAttributes, strokeAttributes)); + primitive2d::Primitive2DContainer primitives; + primitives.push_back(strokePrimitive); + processor->process(primitives); + metafile.Stop(); + metafile.WindStart(); + + // Now verify that the metafile has the one PolyLine action with the right dashing. + int lineActionCount = 0; + for (std::size_t i = 0; i < metafile.GetActionSize(); ++i) + { + const MetaAction* metaAction = metafile.GetAction(i); + if (metaAction->GetType() == MetaActionType::POLYLINE) + { + const MetaPolyLineAction* action + = static_cast<const MetaPolyLineAction*>(metaAction); + + CPPUNIT_ASSERT_EQUAL(35.0, action->GetLineInfo().GetWidth()); + CPPUNIT_ASSERT_EQUAL(LineStyle::Dash, action->GetLineInfo().GetStyle()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), action->GetLineInfo().GetDashCount()); + CPPUNIT_ASSERT_EQUAL(0.35, action->GetLineInfo().GetDashLen()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), action->GetLineInfo().GetDotCount()); + CPPUNIT_ASSERT_EQUAL(0.0, action->GetLineInfo().GetDotLen()); + CPPUNIT_ASSERT_EQUAL(69.65, action->GetLineInfo().GetDistance()); + lineActionCount++; + } + } + CPPUNIT_ASSERT_EQUAL(1, lineActionCount); + + // Now draw the metafile using canvas and verify that the line is drawn. + setupCanvas(Size(1920, 1080)); + cppcanvas::CanvasSharedPtr cppCanvas = cppcanvas::VCLFactory::createCanvas(mCanvas); + // I got these matrices from a breakpoint in drawing the polyline, and walking up + // the stack to the canvas code. + cppCanvas->setTransformation( + basegfx::B2DHomMatrix(0.056662828121770453, 0, 0, 0, 0.056640419947506564, 0)); + cppcanvas::RendererSharedPtr renderer = cppcanvas::VCLFactory::createRenderer( + cppCanvas, metafile, cppcanvas::Renderer::Parameters()); + renderer->setTransformation(basegfx::B2DHomMatrix(14548, 0, -2, 0, 3350, 3431)); + CPPUNIT_ASSERT(renderer->draw()); + exportDevice(u"test-tdf136957"_ustr, mVclDevice); + Bitmap bitmap = mVclDevice->GetBitmap(Point(), Size(1920, 1080)); + BitmapScopedReadAccess access(bitmap); + // There should be a dotted line, without the fix it wouldn't be there, so check + // there's a sufficient amount of non-white pixels and that's the line. + int nonWhiteCount = 0; + for (tools::Long y = 193; y <= 524; ++y) + for (tools::Long x = 883; x <= 885; ++x) + if (access->GetColor(y, x) != COL_WHITE) + ++nonWhiteCount; + CPPUNIT_ASSERT_GREATER(100, nonWhiteCount); + } + + CPPUNIT_TEST_SUITE(VclMetaFileProcessor2DTest); + CPPUNIT_TEST(testTdf136957); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VclMetaFileProcessor2DTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx new file mode 100644 index 000000000000..bdb46ffeb5af --- /dev/null +++ b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx @@ -0,0 +1,138 @@ +/* -*- 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 <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/graphicfilter.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/utils/gradienttools.hxx> + +using namespace drawinglayer; + +class VclPixelProcessor2DTest : public test::BootstrapFixture +{ + // if enabled - check the result images with: + // "xdg-open ./workdir/CppunitTest/drawinglayer_processors.test.core/" + static constexpr const bool mbExportBitmap = false; + + void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device) + { + if (mbExportBitmap) + { + Bitmap aBitmap(device->GetBitmap(Point(0, 0), device->GetOutputSizePixel())); + SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmap, aStream); + } + } + +public: + VclPixelProcessor2DTest() + : BootstrapFixture(true, false) + { + } + + // Test that drawing only a part of a gradient draws the proper part of it. + void testTdf139000() + { + ScopedVclPtr<VirtualDevice> device + = VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA); + device->SetOutputSizePixel(Size(100, 200)); + device->SetBackground(Wallpaper(COL_RED)); + device->Erase(); + + drawinglayer::geometry::ViewInformation2D view; + std::unique_ptr<processor2d::BaseProcessor2D> processor( + processor2d::createProcessor2DFromOutputDevice(*device, view)); + CPPUNIT_ASSERT(processor); + + // I stumbled over this when hunting another problem, but I have to correct + // this: This test does test something that is not supported. It seems to be + // based on the *misunderstanding* that in the version of the constructor of + // FillGradientPrimitive2D (and similar others) with two ranges the 2nd + // B2DRange parameter 'OutputRange' is a 'clipping' parameter. This is *not* + // the case --- it is in fact the *contrary*, it is there to *extend* the + // usual definition/paintRange of a gradient: + // It was originally needed to correctly display TextFrames (TF) in Writer: If you + // have a TF in SW filled with a gradient and that TF has sub-frames, it inherits + // the gradient fill. Since you can freely move those sub-TFs even outside the + // parent TF there has to be a way to not only paint gradients in their definition + // range (classical, all DrawObjects do that), but extended from that. This is + // needed e.g. for linear gradients, but - dependent of e.g. the center settings - + // also for all other ones, all can have geometry 'outside' the DefinitionRange. + // This is now also used in various other locations which is proof that this is + // useful and needed. It is possible to see that basic history/reason for this + // parameter by following the git history and why and under which circumstances + // that parameter was originally added. Other hints are: It is *not* named + // 'ClipRange'. Using a B2DRange to define a ClipRange topology would be bad due + // to not being transformable, a PolyPolygon would be used in that case. Using as + // clipping mechanism would offer a 2nd principle to add clipping for primitives + // besides MaskPrimitive2D - always bad style in a sub-system. A quick look + // on it's usages gives hints, too. + // This means that when defining an outputRange that resides completely *inside* + // the definitionRange *no change* at all is done by definition since this does + // not *extend* the target area of the gradient paint region at all. If an + // implementation does clip and limit output to 'outputRange' that should do no + // harm, but is not the expected/reliable way to paint primitives clipped. + // That's why all DrawObjects with gradient fill (and other fills do the same) + // embed the fill that is defined for a range (usually the BoundRange of a + // PolyPolygon) in a MaskPrimitive2D defined by the outline PolyPolygon of the + // shape. Nothing speaks against renderers detecting that combination and do + // something optimized if they want to, especially SDPRs, but this is not + // required. The standard embedded clipping of the implementations of the + // MaskPrimitive2D do the right thing. + // This test intends to paint the lower part of a gradient, so define the + // gradient for the full target range and embed it to a MaskPrimitive2D + // defining the lower part of that area to do that. + + basegfx::B2DRange definitionRange(0, 0, 100, 200); + basegfx::B2DRange outputRange(0, 100, 100, 200); // Paint only lower half of the gradient. + + const primitive2d::Primitive2DContainer primitives{ + rtl::Reference<primitive2d::MaskPrimitive2D>(new primitive2d::MaskPrimitive2D( + basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(outputRange)), + primitive2d::Primitive2DContainer{ + rtl::Reference<primitive2d::FillGradientPrimitive2D>( + new primitive2d::FillGradientPrimitive2D( + definitionRange, attribute::FillGradientAttribute( + css::awt::GradientStyle_LINEAR, 0, 0, 0, 0, + basegfx::BColorStops(COL_WHITE.getBColor(), + COL_BLACK.getBColor())))) })) + }; + processor->process(primitives); + + exportDevice(u"test-tdf139000.png"_ustr, device); + Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel()); + BitmapScopedReadAccess access(bitmap); + // The upper half should keep its red background color. + CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_RED), access->GetColor(Point(0, 99))); + // First line of the gradient should not be the start color, but something halfway. + CPPUNIT_ASSERT_LESS(static_cast<sal_uInt16>(16), + access->GetColor(Point(0, 100)).GetColorError(COL_GRAY)); + // Last line of the gradient should be the end color, or close. + CPPUNIT_ASSERT_LESS(static_cast<sal_uInt16>(16), + access->GetColor(Point(0, 199)).GetColorError(COL_BLACK)); + } + + CPPUNIT_TEST_SUITE(VclPixelProcessor2DTest); + CPPUNIT_TEST(testTdf139000); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VclPixelProcessor2DTest); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/animation/animationtiming.cxx b/drawinglayer/source/animation/animationtiming.cxx index 7be8ac2d94e8..364ae899c9ca 100644 --- a/drawinglayer/source/animation/animationtiming.cxx +++ b/drawinglayer/source/animation/animationtiming.cxx @@ -116,7 +116,7 @@ namespace drawinglayer::animation double AnimationEntryLinear::getStateAtTime(double fTime) const { - if(basegfx::fTools::more(mfDuration, 0.0)) + if(mfDuration > 0.0) { const double fFactor(fTime / mfDuration); @@ -193,19 +193,15 @@ namespace drawinglayer::animation bool AnimationEntryList::operator==(const AnimationEntry& rCandidate) const { - const AnimationEntryList* pCompare = dynamic_cast< const AnimationEntryList* >(&rCandidate); + const AnimationEntryList* pCompare = dynamic_cast<const AnimationEntryList*>(&rCandidate); - if(pCompare && mfDuration == pCompare->mfDuration) + if (pCompare && mfDuration == pCompare->mfDuration) { - for(size_t a(0); a < maEntries.size(); a++) - { - if(!(*maEntries[a] == *pCompare->maEntries[a])) - { - return false; - } - } - - return true; + return std::equal(maEntries.cbegin(), maEntries.cend(), + pCompare->maEntries.cbegin(), pCompare->maEntries.cend(), + [](const auto& lhs, const auto& rhs) { + return *lhs == *rhs; + }); } return false; @@ -263,8 +259,7 @@ namespace drawinglayer::animation AnimationEntryLoop::AnimationEntryLoop(sal_uInt32 nRepeat) - : AnimationEntryList(), - mnRepeat(nRepeat) + : mnRepeat(nRepeat) { } diff --git a/drawinglayer/source/attribute/fillgradientattribute.cxx b/drawinglayer/source/attribute/fillgradientattribute.cxx index fe38a07d41ac..54f455f647ef 100644 --- a/drawinglayer/source/attribute/fillgradientattribute.cxx +++ b/drawinglayer/source/attribute/fillgradientattribute.cxx @@ -18,9 +18,7 @@ */ #include <drawinglayer/attribute/fillgradientattribute.hxx> -#include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> - +#include <basegfx/utils/gradienttools.hxx> namespace drawinglayer::attribute { @@ -28,55 +26,68 @@ namespace drawinglayer::attribute { public: // data definitions - GradientStyle meStyle; double mfBorder; double mfOffsetX; double mfOffsetY; double mfAngle; - basegfx::BColor maStartColor; - basegfx::BColor maEndColor; + basegfx::BColorStops maColorStops; + css::awt::GradientStyle meStyle; sal_uInt16 mnSteps; ImpFillGradientAttribute( - GradientStyle eStyle, + css::awt::GradientStyle eStyle, double fBorder, double fOffsetX, double fOffsetY, double fAngle, - const basegfx::BColor& rStartColor, - const basegfx::BColor& rEndColor, + const basegfx::BColorStops& rColorStops, sal_uInt16 nSteps) - : meStyle(eStyle), - mfBorder(fBorder), + : mfBorder(fBorder), mfOffsetX(fOffsetX), mfOffsetY(fOffsetY), mfAngle(fAngle), - maStartColor(rStartColor), - maEndColor(rEndColor), + maColorStops(rColorStops), // copy ColorStops + meStyle(eStyle), mnSteps(nSteps) { + // Correct the local ColorStops. That will guarantee that the + // content does contain no offsets < 0.0, > 1.0 or double + // ones, also secures sorted arrangement and checks for + // double colors, too (see there for more information). + // This is what the usages of this in primitives need. + // Since FillGradientAttribute is read-only doing this + // once here in the constructor is sufficient + maColorStops.sortAndCorrect(); + + // sortAndCorrectColorStops is rigid and can return + // an empty result. To keep things simple, add a single + // fallback value + if (maColorStops.empty()) + { + maColorStops.emplace_back(0.0, basegfx::BColor()); + } } ImpFillGradientAttribute() - : meStyle(GradientStyle::Linear), - mfBorder(0.0), + : mfBorder(0.0), mfOffsetX(0.0), mfOffsetY(0.0), mfAngle(0.0), - maStartColor(basegfx::BColor()), - maEndColor(basegfx::BColor()), + maColorStops(), + meStyle(css::awt::GradientStyle_LINEAR), mnSteps(0) { + // always add a fallback color, see above + maColorStops.emplace_back(0.0, basegfx::BColor()); } // data read access - GradientStyle getStyle() const { return meStyle; } + css::awt::GradientStyle getStyle() const { return meStyle; } double getBorder() const { return mfBorder; } double getOffsetX() const { return mfOffsetX; } double getOffsetY() const { return mfOffsetY; } double getAngle() const { return mfAngle; } - const basegfx::BColor& getStartColor() const { return maStartColor; } - const basegfx::BColor& getEndColor() const { return maEndColor; } + const basegfx::BColorStops& getColorStops() const { return maColorStops; } sal_uInt16 getSteps() const { return mnSteps; } bool operator==(const ImpFillGradientAttribute& rCandidate) const @@ -86,34 +97,35 @@ namespace drawinglayer::attribute && getOffsetX() == rCandidate.getOffsetX() && getOffsetY() == rCandidate.getOffsetY() && getAngle() == rCandidate.getAngle() - && getStartColor() == rCandidate.getStartColor() - && getEndColor() == rCandidate.getEndColor() + && getColorStops() == rCandidate.getColorStops() && getSteps() == rCandidate.getSteps()); } }; namespace { - struct theGlobalDefault : - public rtl::Static< FillGradientAttribute::ImplType, theGlobalDefault > {}; + FillGradientAttribute::ImplType& theGlobalDefault() + { + static FillGradientAttribute::ImplType SINGLETON; + return SINGLETON; + } } FillGradientAttribute::FillGradientAttribute( - GradientStyle eStyle, + css::awt::GradientStyle eStyle, double fBorder, double fOffsetX, double fOffsetY, double fAngle, - const basegfx::BColor& rStartColor, - const basegfx::BColor& rEndColor, + const basegfx::BColorStops& rColorStops, sal_uInt16 nSteps) : mpFillGradientAttribute(ImpFillGradientAttribute( - eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rStartColor, rEndColor, nSteps)) + eStyle, fBorder, fOffsetX, fOffsetY, fAngle, rColorStops, nSteps)) { } FillGradientAttribute::FillGradientAttribute() - : mpFillGradientAttribute(theGlobalDefault::get()) + : mpFillGradientAttribute(theGlobalDefault()) { } @@ -125,7 +137,42 @@ namespace drawinglayer::attribute bool FillGradientAttribute::isDefault() const { - return mpFillGradientAttribute.same_object(theGlobalDefault::get()); + return mpFillGradientAttribute.same_object(theGlobalDefault()); + } + + // MCGR: Check if rendering cannot be handled by old vcl stuff + bool FillGradientAttribute::cannotBeHandledByVCL() const + { + // MCGR: If GradientStops are used, use decomposition since vcl is not able + // to render multi-color gradients + if (getColorStops().size() != 2) + { + return true; + } + + // MCGR: If GradientStops do not start and stop at traditional Start/EndColor, + // use decomposition since vcl is not able to render this + if (!getColorStops().empty()) + { + if (!basegfx::fTools::equalZero(getColorStops().front().getStopOffset()) + || !basegfx::fTools::equal(getColorStops().back().getStopOffset(), 1.0)) + { + return true; + } + } + + // VCL should be able to handle all styles, but for tdf#133477 the VCL result + // is different from processing the gradient manually by drawinglayer + // (and the Writer unittest for it fails). Keep using the drawinglayer code + // until somebody founds out what's wrong and fixes it. + if (getStyle() != css::awt::GradientStyle_LINEAR + && getStyle() != css::awt::GradientStyle_AXIAL + && getStyle() != css::awt::GradientStyle_RADIAL) + { + return true; + } + + return false; } FillGradientAttribute& FillGradientAttribute::operator=(const FillGradientAttribute&) = default; @@ -141,14 +188,9 @@ namespace drawinglayer::attribute return rCandidate.mpFillGradientAttribute == mpFillGradientAttribute; } - const basegfx::BColor& FillGradientAttribute::getStartColor() const - { - return mpFillGradientAttribute->getStartColor(); - } - - const basegfx::BColor& FillGradientAttribute::getEndColor() const + const basegfx::BColorStops& FillGradientAttribute::getColorStops() const { - return mpFillGradientAttribute->getEndColor(); + return mpFillGradientAttribute->getColorStops(); } double FillGradientAttribute::getBorder() const @@ -171,7 +213,7 @@ namespace drawinglayer::attribute return mpFillGradientAttribute->getAngle(); } - GradientStyle FillGradientAttribute::getStyle() const + css::awt::GradientStyle FillGradientAttribute::getStyle() const { return mpFillGradientAttribute->getStyle(); } @@ -181,6 +223,39 @@ namespace drawinglayer::attribute return mpFillGradientAttribute->getSteps(); } + bool FillGradientAttribute::sameDefinitionThanAlpha(const FillGradientAttribute& rAlpha) const + { + // entries that are used by all gradient styles + if (getStyle() != rAlpha.getStyle() + || getBorder() != rAlpha.getBorder() + || getSteps() != rAlpha.getSteps()) + { + return false; + } + + // check for offsets if not ignored + const bool bIgnoreOffset(css::awt::GradientStyle_LINEAR == getStyle() || css::awt::GradientStyle_AXIAL == getStyle()); + if (!bIgnoreOffset && (getOffsetX() != rAlpha.getOffsetX() || getOffsetY() != rAlpha.getOffsetY())) + { + return false; + } + + // check for angle if not ignored + const bool bIgnoreAngle(css::awt::GradientStyle_RADIAL == getStyle()); + if (!bIgnoreAngle && getAngle() != rAlpha.getAngle()) + { + return false; + } + + // check for same count & offsets in the gradients (all except 'colors') + if (!getColorStops().sameSizeAndDistances(rAlpha.getColorStops())) + { + return false; + } + + return true; + } + } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/attribute/fillgraphicattribute.cxx b/drawinglayer/source/attribute/fillgraphicattribute.cxx index fa9b4fd57138..5a401dbc0133 100644 --- a/drawinglayer/source/attribute/fillgraphicattribute.cxx +++ b/drawinglayer/source/attribute/fillgraphicattribute.cxx @@ -22,6 +22,7 @@ #include <algorithm> #include <drawinglayer/attribute/fillgraphicattribute.hxx> +#include <utility> #include <vcl/graph.hxx> namespace drawinglayer::attribute @@ -41,12 +42,12 @@ namespace drawinglayer::attribute double mfOffsetY; ImpFillGraphicAttribute( - const Graphic& rGraphic, + Graphic aGraphic, const basegfx::B2DRange& rGraphicRange, bool bTiling, double fOffsetX, double fOffsetY) - : maGraphic(rGraphic), + : maGraphic(std::move(aGraphic)), maGraphicRange(rGraphicRange), mbTiling(bTiling), mfOffsetX(fOffsetX), @@ -57,13 +58,11 @@ namespace drawinglayer::attribute // available when a renderer works with multi-threading. // When changing this, please check if it is still possible to // use a metafile as texture for a 3D object - maGraphic.GetBitmapEx(); + maGraphic.GetBitmap(); } ImpFillGraphicAttribute() - : maGraphic(Graphic()), - maGraphicRange(basegfx::B2DRange()), - mbTiling(false), + : mbTiling(false), mfOffsetX(0.0), mfOffsetY(0.0) { @@ -88,8 +87,16 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< FillGraphicAttribute::ImplType, theGlobalDefault > {}; + FillGraphicAttribute::ImplType& theGlobalDefault() + { + static FillGraphicAttribute::ImplType SINGLETON; + return SINGLETON; + } + } + + FillGraphicAttribute::FillGraphicAttribute() + : mpFillGraphicAttribute(theGlobalDefault()) + { } FillGraphicAttribute::FillGraphicAttribute( @@ -111,7 +118,7 @@ namespace drawinglayer::attribute bool FillGraphicAttribute::isDefault() const { - return mpFillGraphicAttribute.same_object(theGlobalDefault::get()); + return mpFillGraphicAttribute.same_object(theGlobalDefault()); } FillGraphicAttribute& FillGraphicAttribute::operator=(const FillGraphicAttribute&) = default; diff --git a/drawinglayer/source/attribute/fillhatchattribute.cxx b/drawinglayer/source/attribute/fillhatchattribute.cxx index 69dd76c53d97..bc892a024842 100644 --- a/drawinglayer/source/attribute/fillhatchattribute.cxx +++ b/drawinglayer/source/attribute/fillhatchattribute.cxx @@ -19,7 +19,6 @@ #include <drawinglayer/attribute/fillhatchattribute.hxx> #include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -56,7 +55,6 @@ namespace drawinglayer::attribute : meStyle(HatchStyle::Single), mfDistance(0.0), mfAngle(0.0), - maColor(basegfx::BColor()), mnMinimalDiscreteDistance(3), // same as VCL mbFillBackground(false) { @@ -83,8 +81,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< FillHatchAttribute::ImplType, theGlobalDefault > {}; + FillHatchAttribute::ImplType& theGlobalDefault() + { + static FillHatchAttribute::ImplType SINGLETON; + return SINGLETON; + } } FillHatchAttribute::FillHatchAttribute( @@ -101,7 +102,7 @@ namespace drawinglayer::attribute } FillHatchAttribute::FillHatchAttribute() - : mpFillHatchAttribute(theGlobalDefault::get()) + : mpFillHatchAttribute(theGlobalDefault()) { } @@ -113,7 +114,7 @@ namespace drawinglayer::attribute bool FillHatchAttribute::isDefault() const { - return mpFillHatchAttribute.same_object(theGlobalDefault::get()); + return mpFillHatchAttribute.same_object(theGlobalDefault()); } FillHatchAttribute& FillHatchAttribute::operator=(const FillHatchAttribute&) = default; diff --git a/drawinglayer/source/attribute/fontattribute.cxx b/drawinglayer/source/attribute/fontattribute.cxx index 33903003f8c7..c1f0ab000d86 100644 --- a/drawinglayer/source/attribute/fontattribute.cxx +++ b/drawinglayer/source/attribute/fontattribute.cxx @@ -18,185 +18,134 @@ */ #include <drawinglayer/attribute/fontattribute.hxx> -#include <rtl/instance.hxx> #include <rtl/ustring.hxx> - +#include <utility> namespace drawinglayer::attribute { - class ImpFontAttribute - { - public: - /// core data - OUString maFamilyName; // Font Family Name - OUString maStyleName; // Font Style Name - sal_uInt16 mnWeight; // Font weight - - bool mbSymbol : 1; // Symbol Font Flag - bool mbVertical : 1; // Vertical Text Flag - bool mbItalic : 1; // Italic Flag - bool mbOutline : 1; // Outline Flag - bool mbRTL : 1; // RTL Flag - bool mbBiDiStrong : 1; // BiDi Flag - bool mbMonospaced : 1; - - ImpFontAttribute( - const OUString& rFamilyName, - const OUString& rStyleName, - sal_uInt16 nWeight, - bool bSymbol, - bool bVertical, - bool bItalic, - bool bMonospaced, - bool bOutline, - bool bRTL, - bool bBiDiStrong) - : maFamilyName(rFamilyName), - maStyleName(rStyleName), - mnWeight(nWeight), - mbSymbol(bSymbol), - mbVertical(bVertical), - mbItalic(bItalic), - mbOutline(bOutline), - mbRTL(bRTL), - mbBiDiStrong(bBiDiStrong), - mbMonospaced(bMonospaced) - { - } - - ImpFontAttribute() - : maFamilyName(), - maStyleName(), - mnWeight(0), - mbSymbol(false), - mbVertical(false), - mbItalic(false), - mbOutline(false), - mbRTL(false), - mbBiDiStrong(false), - mbMonospaced(false) - { - } - - // data read access - const OUString& getFamilyName() const { return maFamilyName; } - const OUString& getStyleName() const { return maStyleName; } - sal_uInt16 getWeight() const { return mnWeight; } - bool getSymbol() const { return mbSymbol; } - bool getVertical() const { return mbVertical; } - bool getItalic() const { return mbItalic; } - bool getOutline() const { return mbOutline; } - bool getRTL() const { return mbRTL; } - bool getBiDiStrong() const { return mbBiDiStrong; } - bool getMonospaced() const { return mbMonospaced; } - - bool operator==(const ImpFontAttribute& rCompare) const - { - return (getFamilyName() == rCompare.getFamilyName() - && getStyleName() == rCompare.getStyleName() - && getWeight() == rCompare.getWeight() - && getSymbol() == rCompare.getSymbol() - && getVertical() == rCompare.getVertical() - && getItalic() == rCompare.getItalic() - && getOutline() == rCompare.getOutline() - && getRTL() == rCompare.getRTL() - && getBiDiStrong() == rCompare.getBiDiStrong() - && getMonospaced() == rCompare.getMonospaced()); - } - }; - - namespace - { - struct theGlobalDefault : - public rtl::Static< FontAttribute::ImplType, theGlobalDefault > {}; - } - - FontAttribute::FontAttribute( - const OUString& rFamilyName, - const OUString& rStyleName, - sal_uInt16 nWeight, - bool bSymbol, - bool bVertical, - bool bItalic, - bool bMonospaced, - bool bOutline, - bool bRTL, - bool bBiDiStrong) - : mpFontAttribute(ImpFontAttribute( - rFamilyName, rStyleName, nWeight, bSymbol, bVertical, bItalic, bMonospaced, bOutline, bRTL, bBiDiStrong)) - { - } - - FontAttribute::FontAttribute() - : mpFontAttribute(theGlobalDefault::get()) - { - } - - FontAttribute::FontAttribute(const FontAttribute&) = default; - - FontAttribute::FontAttribute(FontAttribute&&) = default; - - FontAttribute::~FontAttribute() = default; - - FontAttribute& FontAttribute::operator=(const FontAttribute&) = default; - - FontAttribute& FontAttribute::operator=(FontAttribute&&) = default; - - bool FontAttribute::operator==(const FontAttribute& rCandidate) const - { - return rCandidate.mpFontAttribute == mpFontAttribute; - } - - const OUString& FontAttribute::getFamilyName() const - { - return mpFontAttribute->getFamilyName(); - } - - const OUString& FontAttribute::getStyleName() const - { - return mpFontAttribute->getStyleName(); - } - - sal_uInt16 FontAttribute::getWeight() const - { - return mpFontAttribute->getWeight(); - } - - bool FontAttribute::getSymbol() const - { - return mpFontAttribute->getSymbol(); - } - - bool FontAttribute::getVertical() const - { - return mpFontAttribute->getVertical(); - } - - bool FontAttribute::getItalic() const - { - return mpFontAttribute->getItalic(); - } - - bool FontAttribute::getOutline() const - { - return mpFontAttribute->getOutline(); - } - - bool FontAttribute::getRTL() const - { - return mpFontAttribute->getRTL(); - } - - bool FontAttribute::getBiDiStrong() const - { - return mpFontAttribute->getBiDiStrong(); - } - - bool FontAttribute::getMonospaced() const - { - return mpFontAttribute->getMonospaced(); - } +class ImpFontAttribute +{ +public: + /// core data + OUString maFamilyName; // Font Family Name + OUString maStyleName; // Font Style Name + sal_uInt16 mnWeight; // Font weight + + bool mbSymbol : 1; // Symbol Font Flag + bool mbVertical : 1; // Vertical Text Flag + bool mbItalic : 1; // Italic Flag + bool mbOutline : 1; // Outline Flag + bool mbRTL : 1; // RTL Flag + bool mbBiDiStrong : 1; // BiDi Flag + bool mbMonospaced : 1; + + ImpFontAttribute(OUString aFamilyName, OUString aStyleName, sal_uInt16 nWeight, bool bSymbol, + bool bVertical, bool bItalic, bool bMonospaced, bool bOutline, bool bRTL, + bool bBiDiStrong) + : maFamilyName(std::move(aFamilyName)) + , maStyleName(std::move(aStyleName)) + , mnWeight(nWeight) + , mbSymbol(bSymbol) + , mbVertical(bVertical) + , mbItalic(bItalic) + , mbOutline(bOutline) + , mbRTL(bRTL) + , mbBiDiStrong(bBiDiStrong) + , mbMonospaced(bMonospaced) + { + } + + ImpFontAttribute() + : mnWeight(0) + , mbSymbol(false) + , mbVertical(false) + , mbItalic(false) + , mbOutline(false) + , mbRTL(false) + , mbBiDiStrong(false) + , mbMonospaced(false) + { + } + + // data read access + const OUString& getFamilyName() const { return maFamilyName; } + const OUString& getStyleName() const { return maStyleName; } + sal_uInt16 getWeight() const { return mnWeight; } + bool getSymbol() const { return mbSymbol; } + bool getVertical() const { return mbVertical; } + bool getItalic() const { return mbItalic; } + bool getOutline() const { return mbOutline; } + bool getRTL() const { return mbRTL; } + bool getBiDiStrong() const { return mbBiDiStrong; } + bool getMonospaced() const { return mbMonospaced; } + + bool operator==(const ImpFontAttribute& rCompare) const + { + return (getFamilyName() == rCompare.getFamilyName() + && getStyleName() == rCompare.getStyleName() && getWeight() == rCompare.getWeight() + && getSymbol() == rCompare.getSymbol() && getVertical() == rCompare.getVertical() + && getItalic() == rCompare.getItalic() && getOutline() == rCompare.getOutline() + && getRTL() == rCompare.getRTL() && getBiDiStrong() == rCompare.getBiDiStrong() + && getMonospaced() == rCompare.getMonospaced()); + } +}; + +namespace +{ +FontAttribute::ImplType& theGlobalDefault() +{ + static FontAttribute::ImplType SINGLETON; + return SINGLETON; +} +} + +FontAttribute::FontAttribute(const OUString& rFamilyName, const OUString& rStyleName, + sal_uInt16 nWeight, bool bSymbol, bool bVertical, bool bItalic, + bool bMonospaced, bool bOutline, bool bRTL, bool bBiDiStrong) + : mpFontAttribute(ImpFontAttribute(rFamilyName, rStyleName, nWeight, bSymbol, bVertical, + bItalic, bMonospaced, bOutline, bRTL, bBiDiStrong)) +{ +} + +FontAttribute::FontAttribute() + : mpFontAttribute(theGlobalDefault()) +{ +} + +FontAttribute::FontAttribute(const FontAttribute&) = default; + +FontAttribute::FontAttribute(FontAttribute&&) = default; + +FontAttribute::~FontAttribute() = default; + +FontAttribute& FontAttribute::operator=(const FontAttribute&) = default; + +FontAttribute& FontAttribute::operator=(FontAttribute&&) = default; + +bool FontAttribute::operator==(const FontAttribute& rCandidate) const +{ + return rCandidate.mpFontAttribute == mpFontAttribute; +} + +const OUString& FontAttribute::getFamilyName() const { return mpFontAttribute->getFamilyName(); } + +const OUString& FontAttribute::getStyleName() const { return mpFontAttribute->getStyleName(); } + +sal_uInt16 FontAttribute::getWeight() const { return mpFontAttribute->getWeight(); } + +bool FontAttribute::getSymbol() const { return mpFontAttribute->getSymbol(); } + +bool FontAttribute::getVertical() const { return mpFontAttribute->getVertical(); } + +bool FontAttribute::getItalic() const { return mpFontAttribute->getItalic(); } + +bool FontAttribute::getOutline() const { return mpFontAttribute->getOutline(); } + +bool FontAttribute::getRTL() const { return mpFontAttribute->getRTL(); } + +bool FontAttribute::getBiDiStrong() const { return mpFontAttribute->getBiDiStrong(); } +bool FontAttribute::getMonospaced() const { return mpFontAttribute->getMonospaced(); } } // end of namespace diff --git a/drawinglayer/source/attribute/lineattribute.cxx b/drawinglayer/source/attribute/lineattribute.cxx index 48d1f73380eb..d4f2418c1354 100644 --- a/drawinglayer/source/attribute/lineattribute.cxx +++ b/drawinglayer/source/attribute/lineattribute.cxx @@ -19,7 +19,6 @@ #include <drawinglayer/attribute/lineattribute.hxx> #include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -49,8 +48,7 @@ namespace drawinglayer::attribute } ImpLineAttribute() - : maColor(basegfx::BColor()), - mfWidth(0.0), + : mfWidth(0.0), meLineJoin(basegfx::B2DLineJoin::Round), meLineCap(css::drawing::LineCap_BUTT), mfMiterMinimumAngle(basegfx::deg2rad(15.0)) @@ -76,8 +74,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< LineAttribute::ImplType, theGlobalDefault > {}; + LineAttribute::ImplType& theGlobalDefault() + { + static LineAttribute::ImplType SINGLETON; + return SINGLETON; + } } LineAttribute::LineAttribute( @@ -97,7 +98,7 @@ namespace drawinglayer::attribute } LineAttribute::LineAttribute() - : mpLineAttribute(theGlobalDefault::get()) + : mpLineAttribute(theGlobalDefault()) { } @@ -107,7 +108,7 @@ namespace drawinglayer::attribute bool LineAttribute::isDefault() const { - return mpLineAttribute.same_object(theGlobalDefault::get()); + return mpLineAttribute.same_object(theGlobalDefault()); } LineAttribute& LineAttribute::operator=(const LineAttribute&) = default; diff --git a/drawinglayer/source/attribute/linestartendattribute.cxx b/drawinglayer/source/attribute/linestartendattribute.cxx index dcfa94f35c39..33ac17c70599 100644 --- a/drawinglayer/source/attribute/linestartendattribute.cxx +++ b/drawinglayer/source/attribute/linestartendattribute.cxx @@ -20,7 +20,7 @@ #include <drawinglayer/attribute/linestartendattribute.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> -#include <rtl/instance.hxx> +#include <utility> namespace drawinglayer::attribute @@ -36,17 +36,16 @@ namespace drawinglayer::attribute ImpLineStartEndAttribute( double fWidth, - const basegfx::B2DPolyPolygon& rPolyPolygon, + basegfx::B2DPolyPolygon aPolyPolygon, bool bCentered) : mfWidth(fWidth), - maPolyPolygon(rPolyPolygon), + maPolyPolygon(std::move(aPolyPolygon)), mbCentered(bCentered) { } ImpLineStartEndAttribute() : mfWidth(0.0), - maPolyPolygon(basegfx::B2DPolyPolygon()), mbCentered(false) { } @@ -66,8 +65,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< LineStartEndAttribute::ImplType, theGlobalDefault > {}; + LineStartEndAttribute::ImplType& theGlobalDefault() + { + static LineStartEndAttribute::ImplType SINGLETON; + return SINGLETON; + } } LineStartEndAttribute::LineStartEndAttribute( @@ -80,7 +82,7 @@ namespace drawinglayer::attribute } LineStartEndAttribute::LineStartEndAttribute() - : mpLineStartEndAttribute(theGlobalDefault::get()) + : mpLineStartEndAttribute(theGlobalDefault()) { } @@ -90,7 +92,7 @@ namespace drawinglayer::attribute bool LineStartEndAttribute::isDefault() const { - return mpLineStartEndAttribute.same_object(theGlobalDefault::get()); + return mpLineStartEndAttribute.same_object(theGlobalDefault()); } LineStartEndAttribute& LineStartEndAttribute::operator=(const LineStartEndAttribute&) = default; diff --git a/drawinglayer/source/attribute/materialattribute3d.cxx b/drawinglayer/source/attribute/materialattribute3d.cxx index f9473b480337..7206ad54bed9 100644 --- a/drawinglayer/source/attribute/materialattribute3d.cxx +++ b/drawinglayer/source/attribute/materialattribute3d.cxx @@ -19,7 +19,6 @@ #include <drawinglayer/attribute/materialattribute3d.hxx> #include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -44,16 +43,12 @@ namespace drawinglayer::attribute explicit ImpMaterialAttribute3D(const basegfx::BColor& rColor) : maColor(rColor), maSpecular(1.0, 1.0, 1.0), - maEmission(), mnSpecularIntensity(15) { } ImpMaterialAttribute3D() - : maColor(basegfx::BColor()), - maSpecular(basegfx::BColor()), - maEmission(basegfx::BColor()), - mnSpecularIntensity(0) + : mnSpecularIntensity(0) { } @@ -74,8 +69,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< MaterialAttribute3D::ImplType, theGlobalDefault > {}; + MaterialAttribute3D::ImplType& theGlobalDefault() + { + static MaterialAttribute3D::ImplType SINGLETON; + return SINGLETON; + } } MaterialAttribute3D::MaterialAttribute3D( @@ -95,7 +93,7 @@ namespace drawinglayer::attribute } MaterialAttribute3D::MaterialAttribute3D() - : mpMaterialAttribute3D(theGlobalDefault::get()) + : mpMaterialAttribute3D(theGlobalDefault()) { } diff --git a/drawinglayer/source/attribute/sdrallattribute3d.cxx b/drawinglayer/source/attribute/sdrallattribute3d.cxx index 079f655796d9..8c74306ca1dd 100644 --- a/drawinglayer/source/attribute/sdrallattribute3d.cxx +++ b/drawinglayer/source/attribute/sdrallattribute3d.cxx @@ -18,21 +18,22 @@ */ #include <drawinglayer/attribute/sdrallattribute3d.hxx> +#include <utility> namespace drawinglayer::attribute { SdrLineFillShadowAttribute3D::SdrLineFillShadowAttribute3D( - const SdrLineAttribute& rLine, - const SdrFillAttribute& rFill, - const SdrLineStartEndAttribute& rLineStartEnd, - const SdrShadowAttribute& rShadow, - const FillGradientAttribute& rFillFloatTransGradient) - : maLine(rLine), - maFill(rFill), - maLineStartEnd(rLineStartEnd), - maShadow(rShadow), - maFillFloatTransGradient(rFillFloatTransGradient) + SdrLineAttribute aLine, + SdrFillAttribute aFill, + SdrLineStartEndAttribute aLineStartEnd, + SdrShadowAttribute aShadow, + FillGradientAttribute aFillFloatTransGradient) + : maLine(std::move(aLine)), + maFill(std::move(aFill)), + maLineStartEnd(std::move(aLineStartEnd)), + maShadow(std::move(aShadow)), + maFillFloatTransGradient(std::move(aFillFloatTransGradient)) { } diff --git a/drawinglayer/source/attribute/sdrfillattribute.cxx b/drawinglayer/source/attribute/sdrfillattribute.cxx index 393df42637c5..8cee8f98d1e9 100644 --- a/drawinglayer/source/attribute/sdrfillattribute.cxx +++ b/drawinglayer/source/attribute/sdrfillattribute.cxx @@ -22,7 +22,7 @@ #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> #include <drawinglayer/attribute/fillhatchattribute.hxx> #include <drawinglayer/attribute/fillgradientattribute.hxx> -#include <rtl/instance.hxx> +#include <utility> namespace drawinglayer::attribute @@ -41,23 +41,19 @@ namespace drawinglayer::attribute ImpSdrFillAttribute( double fTransparence, const basegfx::BColor& rColor, - const FillGradientAttribute& rGradient, - const FillHatchAttribute& rHatch, - const SdrFillGraphicAttribute& rFillGraphic) + FillGradientAttribute aGradient, + FillHatchAttribute aHatch, + SdrFillGraphicAttribute aFillGraphic) : mfTransparence(fTransparence), maColor(rColor), - maGradient(rGradient), - maHatch(rHatch), - maFillGraphic(rFillGraphic) + maGradient(std::move(aGradient)), + maHatch(std::move(aHatch)), + maFillGraphic(std::move(aFillGraphic)) { } ImpSdrFillAttribute() - : mfTransparence(0.0), - maColor(basegfx::BColor()), - maGradient(FillGradientAttribute()), - maHatch(FillHatchAttribute()), - maFillGraphic(SdrFillGraphicAttribute()) + : mfTransparence(0.0) { } @@ -81,8 +77,16 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrFillAttribute::ImplType, theGlobalDefault > {}; + SdrFillAttribute::ImplType& theGlobalDefault() + { + static SdrFillAttribute::ImplType SINGLETON; + return SINGLETON; + } + SdrFillAttribute::ImplType& slideBackgroundFillGlobalDefault() + { + static SdrFillAttribute::ImplType SINGLETON2; + return SINGLETON2; + } } SdrFillAttribute::SdrFillAttribute( @@ -96,8 +100,10 @@ namespace drawinglayer::attribute { } - SdrFillAttribute::SdrFillAttribute() - : mpSdrFillAttribute(theGlobalDefault::get()) + SdrFillAttribute::SdrFillAttribute(bool bSlideBackgroundFill) + : mpSdrFillAttribute(bSlideBackgroundFill + ? slideBackgroundFillGlobalDefault() + : theGlobalDefault()) { } @@ -109,7 +115,12 @@ namespace drawinglayer::attribute bool SdrFillAttribute::isDefault() const { - return mpSdrFillAttribute.same_object(theGlobalDefault::get()); + return mpSdrFillAttribute.same_object(theGlobalDefault()); + } + + bool SdrFillAttribute::isSlideBackgroundFill() const + { + return mpSdrFillAttribute.same_object(slideBackgroundFillGlobalDefault()); } SdrFillAttribute& SdrFillAttribute::operator=(const SdrFillAttribute&) = default; diff --git a/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx index a00c45521e17..b78f3e322c38 100644 --- a/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx +++ b/drawinglayer/source/attribute/sdrfillgraphicattribute.cxx @@ -23,7 +23,7 @@ #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> #include <drawinglayer/attribute/fillgraphicattribute.hxx> -#include <rtl/instance.hxx> +#include <utility> #include <vcl/graph.hxx> @@ -45,7 +45,7 @@ namespace drawinglayer::attribute bool mbLogSize : 1; ImpSdrFillGraphicAttribute( - const Graphic& rFillGraphic, + Graphic aFillGraphic, const basegfx::B2DVector& rGraphicLogicSize, const basegfx::B2DVector& rSize, const basegfx::B2DVector& rOffset, @@ -54,7 +54,7 @@ namespace drawinglayer::attribute bool bTiling, bool bStretch, bool bLogSize) - : maFillGraphic(rFillGraphic), + : maFillGraphic(std::move(aFillGraphic)), maGraphicLogicSize(rGraphicLogicSize), maSize(rSize), maOffset(rOffset), @@ -67,13 +67,7 @@ namespace drawinglayer::attribute } ImpSdrFillGraphicAttribute() - : maFillGraphic(Graphic()), - maGraphicLogicSize(basegfx::B2DVector()), - maSize(basegfx::B2DVector()), - maOffset(basegfx::B2DVector()), - maOffsetPosition(basegfx::B2DVector()), - maRectPoint(basegfx::B2DVector()), - mbTiling(false), + : mbTiling(false), mbStretch(false), mbLogSize(false) { @@ -105,8 +99,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrFillGraphicAttribute::ImplType, theGlobalDefault > {}; + SdrFillGraphicAttribute::ImplType& theGlobalDefault() + { + static SdrFillGraphicAttribute::ImplType SINGLETON; + return SINGLETON; + } } SdrFillGraphicAttribute::SdrFillGraphicAttribute( @@ -134,7 +131,7 @@ namespace drawinglayer::attribute } SdrFillGraphicAttribute::SdrFillGraphicAttribute() - : mpSdrFillGraphicAttribute(theGlobalDefault::get()) + : mpSdrFillGraphicAttribute(theGlobalDefault()) { } @@ -146,7 +143,7 @@ namespace drawinglayer::attribute bool SdrFillGraphicAttribute::isDefault() const { - return mpSdrFillGraphicAttribute.same_object(theGlobalDefault::get()); + return mpSdrFillGraphicAttribute.same_object(theGlobalDefault()); } SdrFillGraphicAttribute& SdrFillGraphicAttribute::operator=(const SdrFillGraphicAttribute&) = default; diff --git a/drawinglayer/source/attribute/sdrglowattribute.cxx b/drawinglayer/source/attribute/sdrglowattribute.cxx index 90367ff67ea0..c27390d64d6d 100644 --- a/drawinglayer/source/attribute/sdrglowattribute.cxx +++ b/drawinglayer/source/attribute/sdrglowattribute.cxx @@ -8,18 +8,8 @@ */ #include <drawinglayer/attribute/sdrglowattribute.hxx> -#include <basegfx/vector/b2dvector.hxx> -#include <basegfx/matrix/b2dhommatrixtools.hxx> -#include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> -#include <utility> - -#include <sal/log.hxx> - -namespace drawinglayer -{ -namespace attribute +namespace drawinglayer::attribute { SdrGlowAttribute::SdrGlowAttribute(sal_Int32 nRadius, const Color& rColor) : m_nRadius(nRadius) @@ -33,8 +23,6 @@ SdrGlowAttribute::SdrGlowAttribute(const SdrGlowAttribute&) = default; SdrGlowAttribute::SdrGlowAttribute(SdrGlowAttribute&&) = default; -SdrGlowAttribute::~SdrGlowAttribute() = default; - SdrGlowAttribute& SdrGlowAttribute::operator=(const SdrGlowAttribute&) = default; SdrGlowAttribute& SdrGlowAttribute::operator=(SdrGlowAttribute&&) = default; @@ -44,7 +32,6 @@ bool SdrGlowAttribute::operator==(const SdrGlowAttribute& rCandidate) const return m_nRadius == rCandidate.m_nRadius && m_color == rCandidate.m_color; } -} // end of namespace attribute -} // end of namespace drawinglayer +} // end of namespace drawinglayer::attribute /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/attribute/sdrglowtextattribute.cxx b/drawinglayer/source/attribute/sdrglowtextattribute.cxx new file mode 100644 index 000000000000..f456955624c3 --- /dev/null +++ b/drawinglayer/source/attribute/sdrglowtextattribute.cxx @@ -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/. + */ + +#include <drawinglayer/attribute/sdrglowtextattribute.hxx> + +namespace drawinglayer::attribute +{ +SdrGlowTextAttribute::SdrGlowTextAttribute(sal_Int32 nTextRadius, const Color& rTextColor) + : m_nTextRadius(nTextRadius) + , m_TextColor(rTextColor) +{ +} + +SdrGlowTextAttribute::SdrGlowTextAttribute() = default; + +SdrGlowTextAttribute::SdrGlowTextAttribute(const SdrGlowTextAttribute&) = default; + +SdrGlowTextAttribute::SdrGlowTextAttribute(SdrGlowTextAttribute&&) = default; + +SdrGlowTextAttribute& SdrGlowTextAttribute::operator=(const SdrGlowTextAttribute&) = default; + +SdrGlowTextAttribute& SdrGlowTextAttribute::operator=(SdrGlowTextAttribute&&) = default; + +bool SdrGlowTextAttribute::operator==(const SdrGlowTextAttribute& rCandidate) const +{ + return m_nTextRadius == rCandidate.m_nTextRadius && m_TextColor == rCandidate.m_TextColor; +} + +} // end of namespace drawinglayer::attribute + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/attribute/sdrlightattribute3d.cxx b/drawinglayer/source/attribute/sdrlightattribute3d.cxx index 56f0b384129a..511590f5ffd4 100644 --- a/drawinglayer/source/attribute/sdrlightattribute3d.cxx +++ b/drawinglayer/source/attribute/sdrlightattribute3d.cxx @@ -20,7 +20,6 @@ #include <drawinglayer/attribute/sdrlightattribute3d.hxx> #include <basegfx/color/bcolor.hxx> #include <basegfx/vector/b3dvector.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -57,12 +56,6 @@ namespace drawinglayer::attribute } }; - namespace - { - struct theGlobalDefault : - public rtl::Static< Sdr3DLightAttribute::ImplType, theGlobalDefault > {}; - } - Sdr3DLightAttribute::Sdr3DLightAttribute( const basegfx::BColor& rColor, const basegfx::B3DVector& rDirection, diff --git a/drawinglayer/source/attribute/sdrlightingattribute3d.cxx b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx index 93b4cf4cec0a..0f625756383c 100644 --- a/drawinglayer/source/attribute/sdrlightingattribute3d.cxx +++ b/drawinglayer/source/attribute/sdrlightingattribute3d.cxx @@ -21,7 +21,6 @@ #include <basegfx/color/bcolor.hxx> #include <basegfx/vector/b3dvector.hxx> #include <drawinglayer/attribute/sdrlightattribute3d.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -35,15 +34,13 @@ namespace drawinglayer::attribute ImpSdrLightingAttribute( const basegfx::BColor& rAmbientLight, - const std::vector< Sdr3DLightAttribute >& rLightVector) + std::vector< Sdr3DLightAttribute >&& rLightVector) : maAmbientLight(rAmbientLight), - maLightVector(rLightVector) + maLightVector(std::move(rLightVector)) { } ImpSdrLightingAttribute() - : maAmbientLight(basegfx::BColor()), - maLightVector(std::vector< Sdr3DLightAttribute >()) { } @@ -60,20 +57,23 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrLightingAttribute::ImplType, theGlobalDefault > {}; + SdrLightingAttribute::ImplType& theGlobalDefault() + { + static SdrLightingAttribute::ImplType SINGLETON; + return SINGLETON; + } } SdrLightingAttribute::SdrLightingAttribute( const basegfx::BColor& rAmbientLight, - const std::vector< Sdr3DLightAttribute >& rLightVector) + std::vector< Sdr3DLightAttribute >&& rLightVector) : mpSdrLightingAttribute(ImpSdrLightingAttribute( - rAmbientLight, rLightVector)) + rAmbientLight, std::move(rLightVector))) { } SdrLightingAttribute::SdrLightingAttribute() - : mpSdrLightingAttribute(theGlobalDefault::get()) + : mpSdrLightingAttribute(theGlobalDefault()) { } @@ -86,7 +86,7 @@ namespace drawinglayer::attribute bool SdrLightingAttribute::isDefault() const { - return mpSdrLightingAttribute.same_object(theGlobalDefault::get()); + return mpSdrLightingAttribute.same_object(theGlobalDefault()); } SdrLightingAttribute& SdrLightingAttribute::operator=(const SdrLightingAttribute&) = default; @@ -107,6 +107,11 @@ namespace drawinglayer::attribute return mpSdrLightingAttribute->getLightVector(); } + const basegfx::BColor& SdrLightingAttribute::getAmbientLightColor() const + { + return mpSdrLightingAttribute->maAmbientLight; + } + // color model solver basegfx::BColor SdrLightingAttribute::solveColorModel( const basegfx::B3DVector& rNormalInEyeCoordinates, @@ -135,7 +140,7 @@ namespace drawinglayer::attribute const Sdr3DLightAttribute& rLight(rLightVector[a]); const double fCosFac(rLight.getDirection().scalar(aEyeNormal)); - if(basegfx::fTools::more(fCosFac, 0.0)) + if(fCosFac > 0.0) { aRetval += (rLight.getColor() * rColor) * fCosFac; @@ -146,7 +151,7 @@ namespace drawinglayer::attribute aSpecularNormal.normalize(); double fCosFac2(aSpecularNormal.scalar(aEyeNormal)); - if(basegfx::fTools::more(fCosFac2, 0.0)) + if(fCosFac2 > 0.0) { fCosFac2 = pow(fCosFac2, static_cast<double>(nSpecularIntensity)); aRetval += rSpecular * fCosFac2; diff --git a/drawinglayer/source/attribute/sdrlineattribute.cxx b/drawinglayer/source/attribute/sdrlineattribute.cxx index 257d934d83bd..f8d4aefb0a95 100644 --- a/drawinglayer/source/attribute/sdrlineattribute.cxx +++ b/drawinglayer/source/attribute/sdrlineattribute.cxx @@ -19,7 +19,6 @@ #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -28,13 +27,13 @@ namespace drawinglayer::attribute { public: // line definitions - basegfx::B2DLineJoin meJoin; // B2DLINEJOIN_* defines double mfWidth; // 1/100th mm, 0.0==hair double mfTransparence; // [0.0 .. 1.0], 0.0==no transp. + double mfFullDotDashLen; // sum of maDotDashArray (for convenience) basegfx::BColor maColor; // color of line + std::vector< double > maDotDashArray; // array of double which defines the dot-dash pattern + basegfx::B2DLineJoin meJoin; // B2DLINEJOIN_* defines css::drawing::LineCap meCap; // BUTT, ROUND, or SQUARE - std::vector< double > maDotDashArray; // array of double which defines the dot-dash pattern - double mfFullDotDashLen; // sum of maDotDashArray (for convenience) ImpSdrLineAttribute( basegfx::B2DLineJoin eJoin, @@ -42,26 +41,24 @@ namespace drawinglayer::attribute double fTransparence, const basegfx::BColor& rColor, css::drawing::LineCap eCap, - const std::vector< double >& rDotDashArray, + std::vector< double >&& rDotDashArray, double fFullDotDashLen) - : meJoin(eJoin), - mfWidth(fWidth), + : mfWidth(fWidth), mfTransparence(fTransparence), + mfFullDotDashLen(fFullDotDashLen), maColor(rColor), - meCap(eCap), - maDotDashArray(rDotDashArray), - mfFullDotDashLen(fFullDotDashLen) + maDotDashArray(std::move(rDotDashArray)), + meJoin(eJoin), + meCap(eCap) { } ImpSdrLineAttribute() - : meJoin(basegfx::B2DLineJoin::Round), - mfWidth(0.0), + : mfWidth(0.0), mfTransparence(0.0), - maColor(basegfx::BColor()), - meCap(css::drawing::LineCap_BUTT), - maDotDashArray(std::vector< double >()), - mfFullDotDashLen(0.0) + mfFullDotDashLen(0.0), + meJoin(basegfx::B2DLineJoin::Round), + meCap(css::drawing::LineCap_BUTT) { } @@ -87,8 +84,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrLineAttribute::ImplType, theGlobalDefault > {}; + SdrLineAttribute::ImplType& theGlobalDefault() + { + static SdrLineAttribute::ImplType SINGLETON; + return SINGLETON; + } } SdrLineAttribute::SdrLineAttribute( @@ -97,7 +97,7 @@ namespace drawinglayer::attribute double fTransparence, const basegfx::BColor& rColor, css::drawing::LineCap eCap, - const std::vector< double >& rDotDashArray, + std::vector< double >&& rDotDashArray, double fFullDotDashLen) : mpSdrLineAttribute( ImpSdrLineAttribute( @@ -106,14 +106,14 @@ namespace drawinglayer::attribute fTransparence, rColor, eCap, - rDotDashArray, + std::move(rDotDashArray), fFullDotDashLen)) { } SdrLineAttribute::SdrLineAttribute() - : mpSdrLineAttribute(theGlobalDefault::get()) + : mpSdrLineAttribute(theGlobalDefault()) { } @@ -125,7 +125,7 @@ namespace drawinglayer::attribute bool SdrLineAttribute::isDefault() const { - return mpSdrLineAttribute.same_object(theGlobalDefault::get()); + return mpSdrLineAttribute.same_object(theGlobalDefault()); } SdrLineAttribute& SdrLineAttribute::operator=(const SdrLineAttribute&) = default; diff --git a/drawinglayer/source/attribute/sdrlinestartendattribute.cxx b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx index c0a88b6f8c0b..911f8aef8bf5 100644 --- a/drawinglayer/source/attribute/sdrlinestartendattribute.cxx +++ b/drawinglayer/source/attribute/sdrlinestartendattribute.cxx @@ -19,7 +19,7 @@ #include <drawinglayer/attribute/sdrlinestartendattribute.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> -#include <rtl/instance.hxx> +#include <utility> namespace drawinglayer::attribute @@ -39,16 +39,16 @@ namespace drawinglayer::attribute bool mbEndCentered : 1; // Line is centered on line end point ImpSdrLineStartEndAttribute( - const basegfx::B2DPolyPolygon& rStartPolyPolygon, - const basegfx::B2DPolyPolygon& rEndPolyPolygon, + basegfx::B2DPolyPolygon aStartPolyPolygon, + basegfx::B2DPolyPolygon aEndPolyPolygon, double fStartWidth, double fEndWidth, bool bStartActive, bool bEndActive, bool bStartCentered, bool bEndCentered) - : maStartPolyPolygon(rStartPolyPolygon), - maEndPolyPolygon(rEndPolyPolygon), + : maStartPolyPolygon(std::move(aStartPolyPolygon)), + maEndPolyPolygon(std::move(aEndPolyPolygon)), mfStartWidth(fStartWidth), mfEndWidth(fEndWidth), mbStartActive(bStartActive), @@ -59,9 +59,7 @@ namespace drawinglayer::attribute } ImpSdrLineStartEndAttribute() - : maStartPolyPolygon(basegfx::B2DPolyPolygon()), - maEndPolyPolygon(basegfx::B2DPolyPolygon()), - mfStartWidth(0.0), + : mfStartWidth(0.0), mfEndWidth(0.0), mbStartActive(false), mbEndActive(false), @@ -95,8 +93,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrLineStartEndAttribute::ImplType, theGlobalDefault > {}; + SdrLineStartEndAttribute::ImplType& theGlobalDefault() + { + static SdrLineStartEndAttribute::ImplType SINGLETON; + return SINGLETON; + } } SdrLineStartEndAttribute::SdrLineStartEndAttribute( @@ -114,7 +115,7 @@ namespace drawinglayer::attribute } SdrLineStartEndAttribute::SdrLineStartEndAttribute() - : mpSdrLineStartEndAttribute(theGlobalDefault::get()) + : mpSdrLineStartEndAttribute(theGlobalDefault()) { } @@ -126,7 +127,7 @@ namespace drawinglayer::attribute bool SdrLineStartEndAttribute::isDefault() const { - return mpSdrLineStartEndAttribute.same_object(theGlobalDefault::get()); + return mpSdrLineStartEndAttribute.same_object(theGlobalDefault()); } SdrLineStartEndAttribute& SdrLineStartEndAttribute::operator=(const SdrLineStartEndAttribute&) = default; diff --git a/drawinglayer/source/attribute/sdrobjectattribute3d.cxx b/drawinglayer/source/attribute/sdrobjectattribute3d.cxx index 78e919d9ab3f..dad8a07eebc5 100644 --- a/drawinglayer/source/attribute/sdrobjectattribute3d.cxx +++ b/drawinglayer/source/attribute/sdrobjectattribute3d.cxx @@ -19,7 +19,6 @@ #include <drawinglayer/attribute/sdrobjectattribute3d.hxx> #include <drawinglayer/attribute/materialattribute3d.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -96,12 +95,6 @@ namespace drawinglayer::attribute } }; - namespace - { - struct theGlobalDefault : - public rtl::Static< Sdr3DObjectAttribute::ImplType, theGlobalDefault > {}; - } - Sdr3DObjectAttribute::Sdr3DObjectAttribute( css::drawing::NormalsKind aNormalsKind, css::drawing::TextureProjectionMode aTextureProjectionX, diff --git a/drawinglayer/source/attribute/sdrsceneattribute3d.cxx b/drawinglayer/source/attribute/sdrsceneattribute3d.cxx index 128abf2027cf..840fe2e3074c 100644 --- a/drawinglayer/source/attribute/sdrsceneattribute3d.cxx +++ b/drawinglayer/source/attribute/sdrsceneattribute3d.cxx @@ -18,7 +18,6 @@ */ #include <drawinglayer/attribute/sdrsceneattribute3d.hxx> -#include <rtl/instance.hxx> namespace drawinglayer::attribute @@ -76,8 +75,11 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< SdrSceneAttribute::ImplType, theGlobalDefault > {}; + SdrSceneAttribute::ImplType& theGlobalDefault() + { + static SdrSceneAttribute::ImplType SINGLETON; + return SINGLETON; + } } SdrSceneAttribute::SdrSceneAttribute( @@ -92,7 +94,7 @@ namespace drawinglayer::attribute } SdrSceneAttribute::SdrSceneAttribute() - : mpSdrSceneAttribute(theGlobalDefault::get()) + : mpSdrSceneAttribute(theGlobalDefault()) { } @@ -104,7 +106,7 @@ namespace drawinglayer::attribute bool SdrSceneAttribute::isDefault() const { - return mpSdrSceneAttribute.same_object(theGlobalDefault::get()); + return mpSdrSceneAttribute.same_object(theGlobalDefault()); } SdrSceneAttribute& SdrSceneAttribute::operator=(const SdrSceneAttribute&) = default; diff --git a/drawinglayer/source/attribute/sdrshadowattribute.cxx b/drawinglayer/source/attribute/sdrshadowattribute.cxx index 9e379acaae28..1eb1b3ea687c 100644 --- a/drawinglayer/source/attribute/sdrshadowattribute.cxx +++ b/drawinglayer/source/attribute/sdrshadowattribute.cxx @@ -20,7 +20,7 @@ #include <drawinglayer/attribute/sdrshadowattribute.hxx> #include <basegfx/vector/b2dvector.hxx> #include <basegfx/color/bcolor.hxx> -#include <rtl/instance.hxx> +#include <docmodel/theme/FormatScheme.hxx> namespace drawinglayer::attribute @@ -33,6 +33,7 @@ namespace drawinglayer::attribute basegfx::B2DVector maSize; // [0.0 .. 2.0] double mfTransparence; // [0.0 .. 1.0], 0.0==no transp. sal_Int32 mnBlur; // [0 .. 180], radius of the blur + model::RectangleAlignment meAlignment{model::RectangleAlignment::Unset}; // alignment of the shadow basegfx::BColor maColor; // color of shadow ImpSdrShadowAttribute( @@ -40,21 +41,20 @@ namespace drawinglayer::attribute const basegfx::B2DVector& rSize, double fTransparence, sal_Int32 nBlur, + model::RectangleAlignment eAlignment, const basegfx::BColor& rColor) : maOffset(rOffset), maSize(rSize), mfTransparence(fTransparence), mnBlur(nBlur), + meAlignment(eAlignment), maColor(rColor) { } ImpSdrShadowAttribute() - : maOffset(basegfx::B2DVector()), - maSize(basegfx::B2DVector()), - mfTransparence(0.0), - mnBlur(0), - maColor(basegfx::BColor()) + : mfTransparence(0.0), + mnBlur(0) { } @@ -71,14 +71,18 @@ namespace drawinglayer::attribute && getSize() == rCandidate.getSize() && getTransparence() == rCandidate.getTransparence() && getBlur() == rCandidate.getBlur() + && meAlignment == rCandidate.meAlignment && getColor() == rCandidate.getColor()); } }; namespace { - struct theGlobalDefault : - public rtl::Static< SdrShadowAttribute::ImplType, theGlobalDefault > {}; + SdrShadowAttribute::ImplType& theGlobalDefault() + { + static SdrShadowAttribute::ImplType SINGLETON; + return SINGLETON; + } } @@ -87,14 +91,15 @@ namespace drawinglayer::attribute const basegfx::B2DVector& rSize, double fTransparence, sal_Int32 nBlur, + model::RectangleAlignment eAlignment, const basegfx::BColor& rColor) : mpSdrShadowAttribute(ImpSdrShadowAttribute( - rOffset, rSize, fTransparence,nBlur, rColor)) + rOffset, rSize, fTransparence, nBlur, eAlignment, rColor)) { } SdrShadowAttribute::SdrShadowAttribute() - : mpSdrShadowAttribute(theGlobalDefault::get()) + : mpSdrShadowAttribute(theGlobalDefault()) { } @@ -106,7 +111,7 @@ namespace drawinglayer::attribute bool SdrShadowAttribute::isDefault() const { - return mpSdrShadowAttribute.same_object(theGlobalDefault::get()); + return mpSdrShadowAttribute.same_object(theGlobalDefault()); } SdrShadowAttribute& SdrShadowAttribute::operator=(const SdrShadowAttribute&) = default; @@ -142,6 +147,11 @@ namespace drawinglayer::attribute return mpSdrShadowAttribute->getBlur(); } + model::RectangleAlignment SdrShadowAttribute::getAlignment() const + { + return mpSdrShadowAttribute->meAlignment; + } + const basegfx::BColor& SdrShadowAttribute::getColor() const { return mpSdrShadowAttribute->getColor(); diff --git a/drawinglayer/source/attribute/strokeattribute.cxx b/drawinglayer/source/attribute/strokeattribute.cxx index 493c906ba91b..d925eb65acbe 100644 --- a/drawinglayer/source/attribute/strokeattribute.cxx +++ b/drawinglayer/source/attribute/strokeattribute.cxx @@ -18,7 +18,6 @@ */ #include <drawinglayer/attribute/strokeattribute.hxx> -#include <rtl/instance.hxx> #include <numeric> @@ -32,16 +31,15 @@ namespace drawinglayer::attribute double mfFullDotDashLen; // sum of maDotDashArray (for convenience) ImpStrokeAttribute( - const std::vector< double >& rDotDashArray, + std::vector< double >&& rDotDashArray, double fFullDotDashLen) - : maDotDashArray(rDotDashArray), + : maDotDashArray(std::move(rDotDashArray)), mfFullDotDashLen(fFullDotDashLen) { } ImpStrokeAttribute() - : maDotDashArray(std::vector< double >()), - mfFullDotDashLen(0.0) + : mfFullDotDashLen(0.0) { } @@ -68,20 +66,23 @@ namespace drawinglayer::attribute namespace { - struct theGlobalDefault : - public rtl::Static< StrokeAttribute::ImplType, theGlobalDefault > {}; + StrokeAttribute::ImplType& theGlobalDefault() + { + static StrokeAttribute::ImplType SINGLETON; + return SINGLETON; + } } StrokeAttribute::StrokeAttribute( - const std::vector< double >& rDotDashArray, + std::vector< double >&& rDotDashArray, double fFullDotDashLen) : mpStrokeAttribute(ImpStrokeAttribute( - rDotDashArray, fFullDotDashLen)) + std::move(rDotDashArray), fFullDotDashLen)) { } StrokeAttribute::StrokeAttribute() - : mpStrokeAttribute(theGlobalDefault::get()) + : mpStrokeAttribute(theGlobalDefault()) { } @@ -93,7 +94,7 @@ namespace drawinglayer::attribute bool StrokeAttribute::isDefault() const { - return mpStrokeAttribute.same_object(theGlobalDefault::get()); + return mpStrokeAttribute.same_object(theGlobalDefault()); } StrokeAttribute& StrokeAttribute::operator=(const StrokeAttribute&) = default; diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx index 66b29591df7e..91f2ad89af3d 100644 --- a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx +++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx @@ -24,16 +24,16 @@ #include <com/sun/star/uno/XComponentContext.hpp> #include <cppuhelper/implbase2.hxx> #include <cppuhelper/supportsservice.hxx> -#include <comphelper/sequence.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <basegfx/numeric/ftools.hxx> -#include <vcl/bitmapex.hxx> +#include <vcl/bitmap.hxx> #include <vcl/canvastools.hxx> #include <com/sun/star/geometry/RealRectangle2D.hpp> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> -#include <converters.hxx> +#include <drawinglayer/converters.hxx> +#include <comphelper/sequenceashashmap.hxx> using namespace ::com::sun::star; @@ -43,7 +43,7 @@ namespace drawinglayer::unorenderer namespace { class XPrimitive2DRenderer: - public cppu::WeakAggImplHelper2< + public cppu::WeakImplHelper< css::graphic::XPrimitive2DRenderer, css::lang::XServiceInfo> { public: @@ -81,6 +81,16 @@ namespace drawinglayer::unorenderer const css::geometry::RealRectangle2D& Range, ::sal_uInt32 MaximumQuadraticPixels) { + o3tl::Length eRangeUnit = o3tl::Length::mm100; + comphelper::SequenceAsHashMap aViewInformationMap(aViewInformationSequence); + auto it = aViewInformationMap.find(u"RangeUnit"_ustr); + if (it != aViewInformationMap.end()) + { + sal_Int32 nVal{}; + it->second >>= nVal; + eRangeUnit = static_cast<o3tl::Length>(nVal); + } + uno::Reference< rendering::XBitmap > XBitmap; if(aPrimitive2DSequence.hasElements()) @@ -89,7 +99,7 @@ namespace drawinglayer::unorenderer const double fWidth(aRange.getWidth()); const double fHeight(aRange.getHeight()); - if(basegfx::fTools::more(fWidth, 0.0) && basegfx::fTools::more(fHeight, 0.0)) + if(fWidth > 0.0 && fHeight > 0.0) { if(0 == DPI_X) { @@ -106,10 +116,17 @@ namespace drawinglayer::unorenderer MaximumQuadraticPixels = 500000; } - const geometry::ViewInformation2D aViewInformation2D(aViewInformationSequence); - const double fFactor100th_mmToInch(1.0 / (2.54 * 1000.0)); - const sal_uInt32 nDiscreteWidth(basegfx::fround((fWidth * fFactor100th_mmToInch) * DPI_X)); - const sal_uInt32 nDiscreteHeight(basegfx::fround((fHeight * fFactor100th_mmToInch) * DPI_Y)); + auto aViewInformation2D = geometry::createViewInformation2D(aViewInformationSequence); + + if(aViewInformation2D.getViewport().isEmpty()) + { + // we have a Viewport since we create a discrete pixel device, use it + // if none is given + aViewInformation2D.setViewport(aRange); + } + + const sal_uInt32 nDiscreteWidth(basegfx::fround(o3tl::convert(fWidth, eRangeUnit, o3tl::Length::in) * DPI_X)); + const sal_uInt32 nDiscreteHeight(basegfx::fround(o3tl::convert(fHeight, eRangeUnit, o3tl::Length::in) * DPI_Y)); basegfx::B2DHomMatrix aEmbedding( basegfx::utils::createTranslateB2DHomMatrix( @@ -123,22 +140,22 @@ namespace drawinglayer::unorenderer const primitive2d::Primitive2DReference xEmbedRef( new primitive2d::TransformPrimitive2D( aEmbedding, - comphelper::sequenceToContainer<primitive2d::Primitive2DContainer>(aPrimitive2DSequence))); - const primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef }; + aPrimitive2DSequence)); + primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef }; - BitmapEx aBitmapEx( - convertToBitmapEx( - xEmbedSeq, + Bitmap aBitmap( + convertToBitmap( + std::move(xEmbedSeq), aViewInformation2D, nDiscreteWidth, nDiscreteHeight, MaximumQuadraticPixels)); - if(!aBitmapEx.IsEmpty()) + if(!aBitmap.IsEmpty()) { - aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); - aBitmapEx.SetPrefSize(Size(basegfx::fround(fWidth), basegfx::fround(fHeight))); - XBitmap = vcl::unotools::xBitmapFromBitmapEx(aBitmapEx); + aBitmap.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + aBitmap.SetPrefSize(Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight))); + XBitmap = vcl::unotools::xBitmapFromBitmap(aBitmap); } } } @@ -148,7 +165,7 @@ namespace drawinglayer::unorenderer OUString SAL_CALL XPrimitive2DRenderer::getImplementationName() { - return "drawinglayer::unorenderer::XPrimitive2DRenderer"; + return u"drawinglayer::unorenderer::XPrimitive2DRenderer"_ustr; } sal_Bool SAL_CALL XPrimitive2DRenderer::supportsService(const OUString& rServiceName) @@ -158,7 +175,7 @@ namespace drawinglayer::unorenderer uno::Sequence< OUString > SAL_CALL XPrimitive2DRenderer::getSupportedServiceNames() { - return { "com.sun.star.graphic.Primitive2DTools" }; + return { u"com.sun.star.graphic.Primitive2DTools"_ustr }; } } // end of namespace diff --git a/drawinglayer/source/dumper/EnhancedShapeDumper.cxx b/drawinglayer/source/dumper/EnhancedShapeDumper.cxx index 84fbc58dbb3b..333bf4bff7c7 100644 --- a/drawinglayer/source/dumper/EnhancedShapeDumper.cxx +++ b/drawinglayer/source/dumper/EnhancedShapeDumper.cxx @@ -19,139 +19,139 @@ using namespace com::sun::star; void EnhancedShapeDumper::dumpEnhancedCustomShapeExtrusionService(const uno::Reference< beans::XPropertySet >& xPropSet) { { - uno::Any anotherAny = xPropSet->getPropertyValue("Extrusion"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Extrusion"_ustr); bool bExtrusion; if(anotherAny >>= bExtrusion) dumpExtrusionAsAttribute(bExtrusion); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Brightness"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Brightness"_ustr); double aBrightness = double(); if(anotherAny >>= aBrightness) dumpBrightnessAsAttribute(aBrightness); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Depth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Depth"_ustr); drawing::EnhancedCustomShapeParameterPair aDepth; if(anotherAny >>= aDepth) dumpDepthAsElement(aDepth); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Diffusion"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Diffusion"_ustr); double aDiffusion = double(); if(anotherAny >>= aDiffusion) dumpDiffusionAsAttribute(aDiffusion); } { - uno::Any anotherAny = xPropSet->getPropertyValue("NumberOfLineSegments"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"NumberOfLineSegments"_ustr); sal_Int32 aNumberOfLineSegments = sal_Int32(); if(anotherAny >>= aNumberOfLineSegments) dumpNumberOfLineSegmentsAsAttribute(aNumberOfLineSegments); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LightFace"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LightFace"_ustr); bool bLightFace; if(anotherAny >>= bLightFace) dumpLightFaceAsAttribute(bLightFace); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightHarsh"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FirstLightHarsh"_ustr); bool bFirstLightHarsh; if(anotherAny >>= bFirstLightHarsh) dumpFirstLightHarshAsAttribute(bFirstLightHarsh); } { - uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightHarsh"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"SecondLightHarsh"_ustr); bool bSecondLightHarsh; if(anotherAny >>= bSecondLightHarsh) dumpSecondLightHarshAsAttribute(bSecondLightHarsh); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightLevel"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FirstLightLevel"_ustr); double aFirstLightLevel = double(); if(anotherAny >>= aFirstLightLevel) dumpFirstLightLevelAsAttribute(aFirstLightLevel); } { - uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightLevel"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"SecondLightLevel"_ustr); double aSecondLightLevel = double(); if(anotherAny >>= aSecondLightLevel) dumpSecondLightLevelAsAttribute(aSecondLightLevel); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FirstLightDirection"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FirstLightDirection"_ustr); drawing::Direction3D aFirstLightDirection; if(anotherAny >>= aFirstLightDirection) dumpFirstLightDirectionAsElement(aFirstLightDirection); } { - uno::Any anotherAny = xPropSet->getPropertyValue("SecondLightDirection"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"SecondLightDirection"_ustr); drawing::Direction3D aSecondLightDirection; if(anotherAny >>= aSecondLightDirection) dumpSecondLightDirectionAsElement(aSecondLightDirection); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Metal"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Metal"_ustr); bool bMetal; if(anotherAny >>= bMetal) dumpMetalAsAttribute(bMetal); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ShadeMode"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ShadeMode"_ustr); drawing::ShadeMode eShadeMode; if(anotherAny >>= eShadeMode) dumpShadeModeAsAttribute(eShadeMode); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RotateAngle"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RotateAngle"_ustr); drawing::EnhancedCustomShapeParameterPair aRotateAngle; if(anotherAny >>= aRotateAngle) dumpRotateAngleAsElement(aRotateAngle); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RotationCenter"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RotationCenter"_ustr); drawing::Direction3D aRotationCenter; if(anotherAny >>= aRotationCenter) dumpRotationCenterAsElement(aRotationCenter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Shininess"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Shininess"_ustr); double aShininess = double(); if(anotherAny >>= aShininess) dumpShininessAsAttribute(aShininess); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Skew"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Skew"_ustr); drawing::EnhancedCustomShapeParameterPair aSkew; if(anotherAny >>= aSkew) dumpSkewAsElement(aSkew); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Specularity"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Specularity"_ustr); double aSpecularity = double(); if(anotherAny >>= aSpecularity) dumpSpecularityAsAttribute(aSpecularity); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ProjectionMode"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ProjectionMode"_ustr); drawing::ProjectionMode eProjectionMode; if(anotherAny >>= eProjectionMode) dumpProjectionModeAsAttribute(eProjectionMode); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ViewPoint"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ViewPoint"_ustr); drawing::Position3D aViewPoint; if(anotherAny >>= aViewPoint) dumpViewPointAsElement(aViewPoint); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Origin"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Origin"_ustr); drawing::EnhancedCustomShapeParameterPair aOrigin; if(anotherAny >>= aOrigin) dumpOriginAsElement(aOrigin); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ExtrusionColor"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ExtrusionColor"_ustr); bool bExtrusionColor; if(anotherAny >>= bExtrusionColor) dumpExtrusionColorAsAttribute(bExtrusionColor); @@ -160,109 +160,109 @@ void EnhancedShapeDumper::dumpEnhancedCustomShapeExtrusionService(const uno::Ref void EnhancedShapeDumper::dumpExtrusionAsAttribute(bool bExtrusion) { if(bExtrusion) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusion"), "%s", "false"); } void EnhancedShapeDumper::dumpBrightnessAsAttribute(double aBrightness) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("brightness"), "%f", aBrightness); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("brightness"), "%f", aBrightness); } void EnhancedShapeDumper::dumpEnhancedCustomShapeParameterPair( const drawing::EnhancedCustomShapeParameterPair& aParameterPair) { { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "First" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "First" )); dumpEnhancedCustomShapeParameter(aParameterPair.First); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Second" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Second" )); dumpEnhancedCustomShapeParameter(aParameterPair.Second); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } } void EnhancedShapeDumper::dumpDepthAsElement(const drawing::EnhancedCustomShapeParameterPair& aDepth) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Depth" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Depth" )); dumpEnhancedCustomShapeParameterPair(aDepth); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpDiffusionAsAttribute(double aDiffusion) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("diffusion"), "%f", aDiffusion); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("diffusion"), "%f", aDiffusion); } void EnhancedShapeDumper::dumpNumberOfLineSegmentsAsAttribute(sal_Int32 aNumberOfLineSegments) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("numberOfLineSegments"), "%" SAL_PRIdINT32, aNumberOfLineSegments); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("numberOfLineSegments"), "%" SAL_PRIdINT32, aNumberOfLineSegments); } void EnhancedShapeDumper::dumpLightFaceAsAttribute(bool bLightFace) { if(bLightFace) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lightFace"), "%s", "false"); } void EnhancedShapeDumper::dumpFirstLightHarshAsAttribute(bool bFirstLightHarsh) { if(bFirstLightHarsh) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightHarsh"), "%s", "false"); } void EnhancedShapeDumper::dumpSecondLightHarshAsAttribute(bool bSecondLightHarsh) { if(bSecondLightHarsh) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightHarsh"), "%s", "false"); } void EnhancedShapeDumper::dumpFirstLightLevelAsAttribute(double aFirstLightLevel) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightLevel"), "%f", aFirstLightLevel); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("firstLightLevel"), "%f", aFirstLightLevel); } void EnhancedShapeDumper::dumpSecondLightLevelAsAttribute(double aSecondLightLevel) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightLevel"), "%f", aSecondLightLevel); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("secondLightLevel"), "%f", aSecondLightLevel); } void EnhancedShapeDumper::dumpDirection3D(drawing::Direction3D aDirection3D) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionX"), "%f", aDirection3D.DirectionX); - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionY"), "%f", aDirection3D.DirectionY); - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionZ"), "%f", aDirection3D.DirectionZ); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionX"), "%f", aDirection3D.DirectionX); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionY"), "%f", aDirection3D.DirectionY); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("directionZ"), "%f", aDirection3D.DirectionZ); } void EnhancedShapeDumper::dumpFirstLightDirectionAsElement(drawing::Direction3D aFirstLightDirection) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FirstLightDirection" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FirstLightDirection" )); dumpDirection3D(aFirstLightDirection); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpSecondLightDirectionAsElement(drawing::Direction3D aSecondLightDirection) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SecondLightDirection" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SecondLightDirection" )); dumpDirection3D(aSecondLightDirection); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpMetalAsAttribute(bool bMetal) { if(bMetal) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("metal"), "%s", "false"); } void EnhancedShapeDumper::dumpShadeModeAsAttribute(drawing::ShadeMode eShadeMode) @@ -270,16 +270,16 @@ void EnhancedShapeDumper::dumpShadeModeAsAttribute(drawing::ShadeMode eShadeMode switch(eShadeMode) { case drawing::ShadeMode_FLAT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "FLAT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "FLAT"); break; case drawing::ShadeMode_PHONG: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "PHONG"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "PHONG"); break; case drawing::ShadeMode_SMOOTH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "SMOOTH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "SMOOTH"); break; case drawing::ShadeMode_DRAFT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "DRAFT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadeMode"), "%s", "DRAFT"); break; default: break; @@ -288,33 +288,33 @@ void EnhancedShapeDumper::dumpShadeModeAsAttribute(drawing::ShadeMode eShadeMode void EnhancedShapeDumper::dumpRotateAngleAsElement(const drawing::EnhancedCustomShapeParameterPair& aRotateAngle) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotateAngle" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotateAngle" )); dumpEnhancedCustomShapeParameterPair(aRotateAngle); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRotationCenterAsElement(drawing::Direction3D aRotationCenter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotationCenter" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RotationCenter" )); dumpDirection3D(aRotationCenter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpShininessAsAttribute(double aShininess) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shininess"), "%f", aShininess); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shininess"), "%f", aShininess); } void EnhancedShapeDumper::dumpSkewAsElement(const drawing::EnhancedCustomShapeParameterPair& aSkew) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Skew" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Skew" )); dumpEnhancedCustomShapeParameterPair(aSkew); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpSpecularityAsAttribute(double aSpecularity) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("specularity"), "%f", aSpecularity); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("specularity"), "%f", aSpecularity); } void EnhancedShapeDumper::dumpProjectionModeAsAttribute(drawing::ProjectionMode eProjectionMode) @@ -322,10 +322,10 @@ void EnhancedShapeDumper::dumpProjectionModeAsAttribute(drawing::ProjectionMode switch(eProjectionMode) { case drawing::ProjectionMode_PARALLEL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PARALLEL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PARALLEL"); break; case drawing::ProjectionMode_PERSPECTIVE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PERSPECTIVE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("projectionMode"), "%s", "PERSPECTIVE"); break; default: break; @@ -334,26 +334,26 @@ void EnhancedShapeDumper::dumpProjectionModeAsAttribute(drawing::ProjectionMode void EnhancedShapeDumper::dumpViewPointAsElement(drawing::Position3D aViewPoint) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewPoint" )); - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionX"), "%f", aViewPoint.PositionX); - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionY"), "%f", aViewPoint.PositionY); - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionZ"), "%f", aViewPoint.PositionZ); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewPoint" )); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionX"), "%f", aViewPoint.PositionX); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionY"), "%f", aViewPoint.PositionY); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("positionZ"), "%f", aViewPoint.PositionZ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpOriginAsElement(const drawing::EnhancedCustomShapeParameterPair& aOrigin) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Origin" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Origin" )); dumpEnhancedCustomShapeParameterPair(aOrigin); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpExtrusionColorAsAttribute(bool bExtrusionColor) { if(bExtrusionColor) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionColor"), "%s", "false"); } @@ -363,233 +363,228 @@ void EnhancedShapeDumper::dumpExtrusionColorAsAttribute(bool bExtrusionColor) void EnhancedShapeDumper::dumpEnhancedCustomShapeGeometryService(const uno::Reference< beans::XPropertySet >& xPropSet) { { - uno::Any anotherAny = xPropSet->getPropertyValue("Type"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Type"_ustr); OUString sType; if(anotherAny >>= sType) dumpTypeAsAttribute(sType); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ViewBox"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ViewBox"_ustr); awt::Rectangle aViewBox; if(anotherAny >>= aViewBox) dumpViewBoxAsElement(aViewBox); } { - uno::Any anotherAny = xPropSet->getPropertyValue("MirroredX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"MirroredX"_ustr); bool bMirroredX; if(anotherAny >>= bMirroredX) dumpMirroredXAsAttribute(bMirroredX); } { - uno::Any anotherAny = xPropSet->getPropertyValue("MirroredY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"MirroredY"_ustr); bool bMirroredY; if(anotherAny >>= bMirroredY) dumpMirroredYAsAttribute(bMirroredY); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextRotateAngle"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextRotateAngle"_ustr); double aTextRotateAngle = double(); if(anotherAny >>= aTextRotateAngle) dumpTextRotateAngleAsAttribute(aTextRotateAngle); } { - uno::Any anotherAny = xPropSet->getPropertyValue("AdjustmentValues"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"AdjustmentValues"_ustr); uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues; if(anotherAny >>= aAdjustmentValues) dumpAdjustmentValuesAsElement(aAdjustmentValues); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Extrusion"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Extrusion"_ustr); uno::Sequence< beans::PropertyValue > aExtrusion; if(anotherAny >>= aExtrusion) dumpExtrusionAsElement(aExtrusion); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Path"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Path"_ustr); uno::Sequence< beans::PropertyValue > aPath; if(anotherAny >>= aPath) dumpPathAsElement(aPath); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextPath"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextPath"_ustr); uno::Sequence< beans::PropertyValue > aTextPath; if(anotherAny >>= aTextPath) dumpTextPathAsElement(aTextPath); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Equations"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Equations"_ustr); uno::Sequence< OUString > aEquations; if(anotherAny >>= aEquations) dumpEquationsAsElement(aEquations); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Handles"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Handles"_ustr); uno::Sequence< beans::PropertyValues > aHandles; if(anotherAny >>= aHandles) dumpHandlesAsElement(aHandles); } } -void EnhancedShapeDumper::dumpTypeAsAttribute(const OUString& sType) +void EnhancedShapeDumper::dumpTypeAsAttribute(std::u16string_view sType) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s", OUStringToOString(sType, RTL_TEXTENCODING_UTF8).getStr()); } void EnhancedShapeDumper::dumpViewBoxAsElement(awt::Rectangle aViewBox) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewBox" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("x"), "%" SAL_PRIdINT32, aViewBox.X); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("y"), "%" SAL_PRIdINT32, aViewBox.Y); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aViewBox.Width); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aViewBox.Height); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ViewBox" )); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("x"), "%" SAL_PRIdINT32, aViewBox.X); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("y"), "%" SAL_PRIdINT32, aViewBox.Y); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aViewBox.Width); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aViewBox.Height); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpMirroredXAsAttribute(bool bMirroredX) { if(bMirroredX) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredX"), "%s", "false"); } void EnhancedShapeDumper::dumpMirroredYAsAttribute(bool bMirroredY) { if(bMirroredY) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("mirroredY"), "%s", "false"); } void EnhancedShapeDumper::dumpTextRotateAngleAsAttribute(double aTextRotateAngle) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textRotateAngle"), "%f", aTextRotateAngle); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textRotateAngle"), "%f", aTextRotateAngle); } void EnhancedShapeDumper::dumpAdjustmentValuesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue>& aAdjustmentValues) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "AdjustmentValues" )); - sal_Int32 nLength = aAdjustmentValues.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "AdjustmentValues" )); + for (const auto& i : aAdjustmentValues) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeAdjustmentValue" )); - uno::Any aAny = aAdjustmentValues[i].Value; + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeAdjustmentValue" )); + uno::Any aAny = i.Value; OUString sValue; float fValue; sal_Int32 nValue; bool bValue; if(aAny >>= sValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); } else if(aAny >>= nValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); } else if(aAny >>= fValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); } else if(aAny >>= bValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); } - switch(aAdjustmentValues[i].State) + switch(i.State) { case beans::PropertyState_DIRECT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); break; case beans::PropertyState_DEFAULT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); break; case beans::PropertyState_AMBIGUOUS_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); break; default: break; } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpPropertyValueAsElement(const beans::PropertyValue& aPropertyValue) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(aPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr()); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, aPropertyValue.Handle); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, aPropertyValue.Handle); uno::Any aAny = aPropertyValue.Value; OUString sValue; if(aAny >>= sValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); } switch(aPropertyValue.State) { case beans::PropertyState_DIRECT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); break; case beans::PropertyState_DEFAULT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); break; case beans::PropertyState_AMBIGUOUS_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); break; default: break; } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpExtrusionAsElement(const uno::Sequence< beans::PropertyValue >& aExtrusion) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Extrusion" )); - sal_Int32 nLength = aExtrusion.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Extrusion" )); + for (const auto& i : aExtrusion) { - dumpPropertyValueAsElement(aExtrusion[i]); + dumpPropertyValueAsElement(i); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpPathAsElement(const uno::Sequence< beans::PropertyValue >& aPath) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Path" )); - sal_Int32 nLength = aPath.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Path" )); + for (const auto& i : aPath) { - dumpPropertyValueAsElement(aPath[i]); + dumpPropertyValueAsElement(i); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpTextPathAsElement(const uno::Sequence< beans::PropertyValue >& aTextPath) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextPath" )); - sal_Int32 nLength = aTextPath.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextPath" )); + for (const auto& i : aTextPath) { - dumpPropertyValueAsElement(aTextPath[i]); + dumpPropertyValueAsElement(i); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpEquationsAsElement(const uno::Sequence< OUString >& aEquations) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Equations" )); - sal_Int32 nLength = aEquations.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Equations" )); + for (const auto& i : aEquations) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", - OUStringToOString(aEquations[i], RTL_TEXTENCODING_UTF8).getStr()); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", + OUStringToOString(i, RTL_TEXTENCODING_UTF8).getStr()); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } // PropertyValues specifies a sequence of PropertyValue instances. @@ -597,20 +592,18 @@ void EnhancedShapeDumper::dumpEquationsAsElement(const uno::Sequence< OUString > // Welcome to Sequenception again. void EnhancedShapeDumper::dumpHandlesAsElement(const uno::Sequence< beans::PropertyValues >& aHandles) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Handles" )); - sal_Int32 nSequenceLength = aHandles.getLength(); - for (sal_Int32 i = 0; i < nSequenceLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Handles" )); + for (const auto& i : aHandles) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValues" )); - uno::Sequence< beans::PropertyValue > propertyValueSequence = aHandles[i]; - sal_Int32 nLength = propertyValueSequence.getLength(); - for (sal_Int32 j = 0; j < nLength; ++j) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValues" )); + uno::Sequence< beans::PropertyValue > propertyValueSequence = i; + for (const auto& j : propertyValueSequence) { - dumpPropertyValueAsElement(propertyValueSequence[j]); + dumpPropertyValueAsElement(j); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } @@ -620,91 +613,91 @@ void EnhancedShapeDumper::dumpHandlesAsElement(const uno::Sequence< beans::Prope void EnhancedShapeDumper::dumpEnhancedCustomShapeHandleService(const uno::Reference< beans::XPropertySet >& xPropSet) { { - uno::Any anotherAny = xPropSet->getPropertyValue("MirroredX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"MirroredX"_ustr); bool bMirroredX; if(anotherAny >>= bMirroredX) dumpMirroredXAsAttribute(bMirroredX); } { - uno::Any anotherAny = xPropSet->getPropertyValue("MirroredY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"MirroredY"_ustr); bool bMirroredY; if(anotherAny >>= bMirroredY) dumpMirroredYAsAttribute(bMirroredY); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Switched"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Switched"_ustr); bool bSwitched; if(anotherAny >>= bSwitched) dumpSwitchedAsAttribute(bSwitched); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Position"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Position"_ustr); drawing::EnhancedCustomShapeParameterPair aPosition; if(anotherAny >>= aPosition) dumpPositionAsElement(aPosition); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Polar"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Polar"_ustr); drawing::EnhancedCustomShapeParameterPair aPolar; if(anotherAny >>= aPolar) dumpPolarAsElement(aPolar); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RefX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RefX"_ustr); sal_Int32 aRefX = sal_Int32(); if(anotherAny >>= aRefX) dumpRefXAsAttribute(aRefX); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RefY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RefY"_ustr); sal_Int32 aRefY = sal_Int32(); if(anotherAny >>= aRefY) dumpRefYAsAttribute(aRefY); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RefAngle"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RefAngle"_ustr); sal_Int32 aRefAngle = sal_Int32(); if(anotherAny >>= aRefAngle) dumpRefAngleAsAttribute(aRefAngle); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RefR"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RefR"_ustr); sal_Int32 aRefR = sal_Int32(); if(anotherAny >>= aRefR) dumpRefRAsAttribute(aRefR); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RangeXMinimum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RangeXMinimum"_ustr); drawing::EnhancedCustomShapeParameter aRangeXMinimum; if(anotherAny >>= aRangeXMinimum) dumpRangeXMinimumAsElement(aRangeXMinimum); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RangeXMaximum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RangeXMaximum"_ustr); drawing::EnhancedCustomShapeParameter aRangeXMaximum; if(anotherAny >>= aRangeXMaximum) dumpRangeXMaximumAsElement(aRangeXMaximum); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RangeYMinimum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RangeYMinimum"_ustr); drawing::EnhancedCustomShapeParameter aRangeYMinimum; if(anotherAny >>= aRangeYMinimum) dumpRangeYMinimumAsElement(aRangeYMinimum); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RangeYMaximum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RangeYMaximum"_ustr); drawing::EnhancedCustomShapeParameter aRangeYMaximum; if(anotherAny >>= aRangeYMaximum) dumpRangeYMaximumAsElement(aRangeYMaximum); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RadiusRangeMinimum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RadiusRangeMinimum"_ustr); drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum; if(anotherAny >>= aRadiusRangeMinimum) dumpRadiusRangeMinimumAsElement(aRadiusRangeMinimum); } { - uno::Any anotherAny = xPropSet->getPropertyValue("RadiusRangeMaximum"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"RadiusRangeMaximum"_ustr); drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum; if(anotherAny >>= aRadiusRangeMaximum) dumpRadiusRangeMaximumAsElement(aRadiusRangeMaximum); @@ -714,43 +707,43 @@ void EnhancedShapeDumper::dumpEnhancedCustomShapeHandleService(const uno::Refere void EnhancedShapeDumper::dumpSwitchedAsAttribute(bool bSwitched) { if(bSwitched) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("switched"), "%s", "false"); } void EnhancedShapeDumper::dumpPositionAsElement(const drawing::EnhancedCustomShapeParameterPair& aPosition) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Position" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Position" )); dumpEnhancedCustomShapeParameterPair(aPosition); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpPolarAsElement(const drawing::EnhancedCustomShapeParameterPair& aPolar) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Polar" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Polar" )); dumpEnhancedCustomShapeParameterPair(aPolar); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRefXAsAttribute(sal_Int32 aRefX) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refX"), "%" SAL_PRIdINT32, aRefX); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refX"), "%" SAL_PRIdINT32, aRefX); } void EnhancedShapeDumper::dumpRefYAsAttribute(sal_Int32 aRefY) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refY"), "%" SAL_PRIdINT32, aRefY); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refY"), "%" SAL_PRIdINT32, aRefY); } void EnhancedShapeDumper::dumpRefAngleAsAttribute(sal_Int32 aRefAngle) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refAngle"), "%" SAL_PRIdINT32, aRefAngle); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refAngle"), "%" SAL_PRIdINT32, aRefAngle); } void EnhancedShapeDumper::dumpRefRAsAttribute(sal_Int32 aRefR) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refR"), "%" SAL_PRIdINT32, aRefR); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("refR"), "%" SAL_PRIdINT32, aRefR); } void EnhancedShapeDumper::dumpEnhancedCustomShapeParameter( @@ -763,65 +756,65 @@ void EnhancedShapeDumper::dumpEnhancedCustomShapeParameter( bool bValue; if(aAny >>= sValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); } else if(aAny >>= nValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); } else if(aAny >>= fValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); } else if(aAny >>= bValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); } sal_Int32 aType = aParameter.Type; - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%" SAL_PRIdINT32, aType); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%" SAL_PRIdINT32, aType); } void EnhancedShapeDumper::dumpRangeXMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeXMinimum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMinimum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMinimum" )); dumpEnhancedCustomShapeParameter(aRangeXMinimum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRangeXMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeXMaximum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMaximum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeXMaximum" )); dumpEnhancedCustomShapeParameter(aRangeXMaximum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRangeYMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeYMinimum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMinimum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMinimum" )); dumpEnhancedCustomShapeParameter(aRangeYMinimum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRangeYMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRangeYMaximum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMaximum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RangeYMaximum" )); dumpEnhancedCustomShapeParameter(aRangeYMaximum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRadiusRangeMinimumAsElement(const drawing::EnhancedCustomShapeParameter& aRadiusRangeMinimum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMinimum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMinimum" )); dumpEnhancedCustomShapeParameter(aRadiusRangeMinimum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpRadiusRangeMaximumAsElement(const drawing::EnhancedCustomShapeParameter& aRadiusRangeMaximum) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMaximum" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "RadiusRangeMaximum" )); dumpEnhancedCustomShapeParameter(aRadiusRangeMaximum); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } @@ -831,73 +824,73 @@ void EnhancedShapeDumper::dumpRadiusRangeMaximumAsElement(const drawing::Enhance void EnhancedShapeDumper::dumpEnhancedCustomShapePathService(const uno::Reference< beans::XPropertySet >& xPropSet) { { - uno::Any anotherAny = xPropSet->getPropertyValue("Coordinates"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Coordinates"_ustr); uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aCoordinates; if(anotherAny >>= aCoordinates) dumpCoordinatesAsElement(aCoordinates); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Segments"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Segments"_ustr); uno::Sequence< drawing::EnhancedCustomShapeSegment > aSegments; if(anotherAny >>= aSegments) dumpSegmentsAsElement(aSegments); } { - uno::Any anotherAny = xPropSet->getPropertyValue("StretchX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"StretchX"_ustr); sal_Int32 aStretchX = sal_Int32(); if(anotherAny >>= aStretchX) dumpStretchXAsAttribute(aStretchX); } { - uno::Any anotherAny = xPropSet->getPropertyValue("StretchY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"StretchY"_ustr); sal_Int32 aStretchY = sal_Int32(); if(anotherAny >>= aStretchY) dumpStretchYAsAttribute(aStretchY); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextFrames"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextFrames"_ustr); uno::Sequence< drawing::EnhancedCustomShapeTextFrame > aTextFrames; if(anotherAny >>= aTextFrames) dumpTextFramesAsElement(aTextFrames); } { - uno::Any anotherAny = xPropSet->getPropertyValue("GluePoints"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"GluePoints"_ustr); uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aGluePoints; if(anotherAny >>= aGluePoints) dumpGluePointsAsElement(aGluePoints); } { - uno::Any anotherAny = xPropSet->getPropertyValue("GluePointLeavingDirections"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"GluePointLeavingDirections"_ustr); uno::Sequence< double > aGluePointLeavingDirections; if(anotherAny >>= aGluePointLeavingDirections) dumpGluePointLeavingDirectionsAsElement(aGluePointLeavingDirections); } { - uno::Any anotherAny = xPropSet->getPropertyValue("GluePointType"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"GluePointType"_ustr); sal_Int32 aGluePointType = sal_Int32(); if(anotherAny >>= aGluePointType) dumpGluePointTypeAsAttribute(aGluePointType); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ExtrusionAllowed"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ExtrusionAllowed"_ustr); bool bExtrusionAllowed; if(anotherAny >>= bExtrusionAllowed) dumpExtrusionAllowedAsAttribute(bExtrusionAllowed); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ConcentricGradientFillAllowed"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ConcentricGradientFillAllowed"_ustr); bool bConcentricGradientFillAllowed; if(anotherAny >>= bConcentricGradientFillAllowed) dumpConcentricGradientFillAllowedAsAttribute(bConcentricGradientFillAllowed); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextPathAllowed"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextPathAllowed"_ustr); bool bTextPathAllowed; if(anotherAny >>= bTextPathAllowed) dumpTextPathAllowedAsAttribute(bTextPathAllowed); } { - uno::Any anotherAny = xPropSet->getPropertyValue("SubViewSize"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"SubViewSize"_ustr); uno::Sequence< awt::Size > aSubViewSize; if(anotherAny >>= aSubViewSize) dumpSubViewSizeAsElement(aSubViewSize); @@ -906,129 +899,123 @@ void EnhancedShapeDumper::dumpEnhancedCustomShapePathService(const uno::Referenc void EnhancedShapeDumper::dumpCoordinatesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeParameterPair >& aCoordinates) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Coordinates" )); - sal_Int32 nLength = aCoordinates.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Coordinates" )); + for (const auto& i : aCoordinates) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" )); - dumpEnhancedCustomShapeParameterPair(aCoordinates[i]); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" )); + dumpEnhancedCustomShapeParameterPair(i); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpSegmentsAsElement(const uno::Sequence< drawing::EnhancedCustomShapeSegment >& aSegments) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Segments" )); - sal_Int32 nLength = aSegments.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Segments" )); + for (const auto& i : aSegments) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeSegment" )); - sal_Int32 aCommand = aSegments[i].Command; - sal_Int32 aCount = aSegments[i].Count; - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("command"), "%" SAL_PRIdINT32, aCommand); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("count"), "%" SAL_PRIdINT32, aCount); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeSegment" )); + sal_Int32 aCommand = i.Command; + sal_Int32 aCount = i.Count; + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("command"), "%" SAL_PRIdINT32, aCommand); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("count"), "%" SAL_PRIdINT32, aCount); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpStretchXAsAttribute(sal_Int32 aStretchX) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchX"), "%" SAL_PRIdINT32, aStretchX); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchX"), "%" SAL_PRIdINT32, aStretchX); } void EnhancedShapeDumper::dumpStretchYAsAttribute(sal_Int32 aStretchY) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchY"), "%" SAL_PRIdINT32, aStretchY); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stretchY"), "%" SAL_PRIdINT32, aStretchY); } void EnhancedShapeDumper::dumpTextFramesAsElement(const uno::Sequence< drawing::EnhancedCustomShapeTextFrame >& aTextFrames) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextFrames" )); - sal_Int32 nLength = aTextFrames.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TextFrames" )); + for (const auto& i : aTextFrames) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeTextFrame" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeTextFrame" )); { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TopLeft" )); - dumpEnhancedCustomShapeParameterPair(aTextFrames[i].TopLeft); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "TopLeft" )); + dumpEnhancedCustomShapeParameterPair(i.TopLeft); + (void)xmlTextWriterEndElement( xmlWriter ); - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "BottomRight" )); - dumpEnhancedCustomShapeParameterPair(aTextFrames[i].BottomRight); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "BottomRight" )); + dumpEnhancedCustomShapeParameterPair(i.BottomRight); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpGluePointsAsElement(const uno::Sequence< drawing::EnhancedCustomShapeParameterPair >& aGluePoints) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePoints" )); - sal_Int32 nLength = aGluePoints.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePoints" )); + for (const auto& i : aGluePoints) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" )); - dumpEnhancedCustomShapeParameterPair(aGluePoints[i]); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "EnhancedCustomShapeParameterPair" )); + dumpEnhancedCustomShapeParameterPair(i); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpGluePointLeavingDirectionsAsElement(const uno::Sequence< double >& aGluePointLeavingDirections) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePointLeavingDirections" )); - sal_Int32 nLength = aGluePointLeavingDirections.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "GluePointLeavingDirections" )); + for (const auto& i : aGluePointLeavingDirections) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("value"), "%f", aGluePointLeavingDirections[i]); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("value"), "%f", i); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void EnhancedShapeDumper::dumpGluePointTypeAsAttribute(sal_Int32 aGluePointType) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("gluePointType"), "%" SAL_PRIdINT32, aGluePointType); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("gluePointType"), "%" SAL_PRIdINT32, aGluePointType); } void EnhancedShapeDumper::dumpExtrusionAllowedAsAttribute(bool bExtrusionAllowed) { if(bExtrusionAllowed) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("extrusionAllowed"), "%s", "false"); } void EnhancedShapeDumper::dumpConcentricGradientFillAllowedAsAttribute(bool bConcentricGradientFillAllowed) { if(bConcentricGradientFillAllowed) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("concentricGradientFillAllowed"), "%s", "false"); } void EnhancedShapeDumper::dumpTextPathAllowedAsAttribute(bool bTextPathAllowed) { if(bTextPathAllowed) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathAllowed"), "%s", "false"); } void EnhancedShapeDumper::dumpSubViewSizeAsElement(const uno::Sequence< awt::Size >& aSubViewSize) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SubViewSize" )); - sal_Int32 nLength = aSubViewSize.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "SubViewSize" )); + for (const auto& i : aSubViewSize) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Size" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aSubViewSize[i].Width); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aSubViewSize[i].Height); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Size" )); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, i.Width); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, i.Height); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } @@ -1038,19 +1025,19 @@ void EnhancedShapeDumper::dumpSubViewSizeAsElement(const uno::Sequence< awt::Siz void EnhancedShapeDumper::dumpEnhancedCustomShapeTextPathService(const uno::Reference< beans::XPropertySet >& xPropSet) { { - uno::Any anotherAny = xPropSet->getPropertyValue("TextPath"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextPath"_ustr); bool bTextPath; if(anotherAny >>= bTextPath) dumpTextPathAsAttribute(bTextPath); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextPathMode"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextPathMode"_ustr); drawing::EnhancedCustomShapeTextPathMode eTextPathMode; if(anotherAny >>= eTextPathMode) dumpTextPathModeAsAttribute(eTextPathMode); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ScaleX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ScaleX"_ustr); bool bScaleX; if(anotherAny >>= bScaleX) dumpScaleXAsAttribute(bScaleX); @@ -1060,9 +1047,9 @@ void EnhancedShapeDumper::dumpEnhancedCustomShapeTextPathService(const uno::Refe void EnhancedShapeDumper::dumpTextPathAsAttribute(bool bTextPath) { if(bTextPath) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPath"), "%s", "false"); } void EnhancedShapeDumper::dumpTextPathModeAsAttribute(drawing::EnhancedCustomShapeTextPathMode eTextPathMode) @@ -1070,13 +1057,13 @@ void EnhancedShapeDumper::dumpTextPathModeAsAttribute(drawing::EnhancedCustomSha switch(eTextPathMode) { case drawing::EnhancedCustomShapeTextPathMode_NORMAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "NORMAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "NORMAL"); break; case drawing::EnhancedCustomShapeTextPathMode_PATH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "PATH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "PATH"); break; case drawing::EnhancedCustomShapeTextPathMode_SHAPE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "SHAPE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textPathMode"), "%s", "SHAPE"); break; default: break; @@ -1086,9 +1073,9 @@ void EnhancedShapeDumper::dumpTextPathModeAsAttribute(drawing::EnhancedCustomSha void EnhancedShapeDumper::dumpScaleXAsAttribute(bool bScaleX) { if(bScaleX) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("scaleX"), "%s", "false"); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/dumper/EnhancedShapeDumper.hxx b/drawinglayer/source/dumper/EnhancedShapeDumper.hxx index 9de2ca3e9097..f6c8453e9087 100644 --- a/drawinglayer/source/dumper/EnhancedShapeDumper.hxx +++ b/drawinglayer/source/dumper/EnhancedShapeDumper.hxx @@ -6,8 +6,11 @@ * 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/. */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_DUMPER_ENHANCEDSHAPEDUMPER_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_DUMPER_ENHANCEDSHAPEDUMPER_HXX +#pragma once + +#include <sal/config.h> + +#include <string_view> #include <libxml/xmlwriter.h> @@ -74,7 +77,7 @@ public: // EnhancedCustomShapeGeometry.idl void dumpEnhancedCustomShapeGeometryService(const css::uno::Reference< css::beans::XPropertySet >& xPropSet); - void dumpTypeAsAttribute(const OUString& sType); + void dumpTypeAsAttribute(std::u16string_view sType); void dumpViewBoxAsElement(css::awt::Rectangle aViewBox); void dumpMirroredXAsAttribute(bool bMirroredX); // also used in EnhancedCustomShapeHandle void dumpMirroredYAsAttribute(bool bMirroredY); // also used in EnhancedCustomShapeHandle @@ -126,6 +129,5 @@ public: private: xmlTextWriterPtr xmlWriter; }; -#endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/dumper/XShapeDumper.cxx b/drawinglayer/source/dumper/XShapeDumper.cxx index be72eeb2b8a9..5a9fef72a941 100644 --- a/drawinglayer/source/dumper/XShapeDumper.cxx +++ b/drawinglayer/source/dumper/XShapeDumper.cxx @@ -42,6 +42,7 @@ #include <rtl/strbuf.hxx> #include <libxml/xmlwriter.h> #include <iostream> +#include <string_view> #include <rtl/ustring.hxx> #define DEBUG_DUMPER 0 @@ -62,9 +63,9 @@ void dumpPropertyValueAsElement(const beans::PropertyValue& rPropertyValue, xmlT void dumpFillStyleAsAttribute(css::drawing::FillStyle eFillStyle, xmlTextWriterPtr xmlWriter); void dumpFillColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter); void dumpFillTransparenceAsAttribute(sal_Int32 aTransparence, xmlTextWriterPtr xmlWriter); -void dumpFillTransparenceGradientNameAsAttribute(const OUString& sTranspGradName, xmlTextWriterPtr xmlWriter); +void dumpFillTransparenceGradientNameAsAttribute(std::u16string_view sTranspGradName, xmlTextWriterPtr xmlWriter); void dumpFillTransparenceGradientAsElement(const css::awt::Gradient& rTranspGrad, xmlTextWriterPtr xmlWriter); -void dumpFillGradientNameAsAttribute(const OUString& sGradName, xmlTextWriterPtr xmlWriter); +void dumpFillGradientNameAsAttribute(std::u16string_view sGradName, xmlTextWriterPtr xmlWriter); void dumpFillGradientAsElement(const css::awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter); void dumpFillHatchAsElement(const css::drawing::Hatch& rHatch, xmlTextWriterPtr xmlWriter); void dumpFillBackgroundAsAttribute(bool bBackground, xmlTextWriterPtr xmlWriter); @@ -84,13 +85,13 @@ void dumpFillBitmapTileAsAttribute(bool bBitmapTile, xmlTextWriterPtr xmlWriter) // LineProperties.idl void dumpLineStyleAsAttribute(css::drawing::LineStyle eLineStyle, xmlTextWriterPtr xmlWriter); void dumpLineDashAsElement(const css::drawing::LineDash& rLineDash, xmlTextWriterPtr xmlWriter); -void dumpLineDashNameAsAttribute(const OUString& sLineDashName, xmlTextWriterPtr xmlWriter); +void dumpLineDashNameAsAttribute(std::u16string_view sLineDashName, xmlTextWriterPtr xmlWriter); void dumpLineColorAsAttribute(sal_Int32 aLineColor, xmlTextWriterPtr xmlWriter); void dumpLineTransparenceAsAttribute(sal_Int32 aLineTransparence, xmlTextWriterPtr xmlWriter); void dumpLineWidthAsAttribute(sal_Int32 aLineWidth, xmlTextWriterPtr xmlWriter); void dumpLineJointAsAttribute(css::drawing::LineJoint eLineJoint, xmlTextWriterPtr xmlWriter); -void dumpLineStartNameAsAttribute(const OUString& sLineStartName, xmlTextWriterPtr xmlWriter); -void dumpLineEndNameAsAttribute(const OUString& sLineEndName, xmlTextWriterPtr xmlWriter); +void dumpLineStartNameAsAttribute(std::u16string_view sLineStartName, xmlTextWriterPtr xmlWriter); +void dumpLineEndNameAsAttribute(std::u16string_view sLineEndName, xmlTextWriterPtr xmlWriter); void dumpLineStartAsElement(const css::drawing::PolyPolygonBezierCoords& rLineStart, xmlTextWriterPtr xmlWriter); void dumpLineEndAsElement(const css::drawing::PolyPolygonBezierCoords& rLineEnd, xmlTextWriterPtr xmlWriter); void dumpLineStartCenterAsAttribute(bool bLineStartCenter, xmlTextWriterPtr xmlWriter); @@ -142,23 +143,24 @@ void dumpShadowYDistanceAsAttribute(sal_Int32 aShadowYDistance, xmlTextWriterPtr //Shape.idl void dumpZOrderAsAttribute(sal_Int32 aZOrder, xmlTextWriterPtr xmlWriter); void dumpLayerIDAsAttribute(sal_Int32 aLayerID, xmlTextWriterPtr xmlWriter); -void dumpLayerNameAsAttribute(const OUString& sLayerName, xmlTextWriterPtr xmlWriter); +void dumpLayerNameAsAttribute(std::u16string_view sLayerName, xmlTextWriterPtr xmlWriter); void dumpVisibleAsAttribute(bool bVisible, xmlTextWriterPtr xmlWriter); void dumpPrintableAsAttribute(bool bPrintable, xmlTextWriterPtr xmlWriter); void dumpMoveProtectAsAttribute(bool bMoveProtect, xmlTextWriterPtr xmlWriter); -void dumpNameAsAttribute(const OUString& sName, xmlTextWriterPtr xmlWriter); +void dumpNameAsAttribute(std::u16string_view sName, xmlTextWriterPtr xmlWriter); void dumpSizeProtectAsAttribute(bool bSizeProtect, xmlTextWriterPtr xmlWriter); void dumpHomogenMatrixLine3(const css::drawing::HomogenMatrixLine3& rLine, xmlTextWriterPtr xmlWriter); void dumpTransformationAsElement(const css::drawing::HomogenMatrix3& rTransformation, xmlTextWriterPtr xmlWriter); void dumpNavigationOrderAsAttribute(sal_Int32 aNavigationOrder, xmlTextWriterPtr xmlWriter); -void dumpHyperlinkAsAttribute(const OUString& sHyperlink, xmlTextWriterPtr xmlWriter); +void dumpHyperlinkAsAttribute(std::u16string_view sHyperlink, xmlTextWriterPtr xmlWriter); void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aInteropGrabBag, xmlTextWriterPtr xmlWriter); // CustomShape.idl -void dumpCustomShapeEngineAsAttribute(const OUString& sCustomShapeEngine, xmlTextWriterPtr xmlWriter); -void dumpCustomShapeDataAsAttribute(const OUString& sCustomShapeData, xmlTextWriterPtr xmlWriter); +void dumpCustomShapeEngineAsAttribute(std::u16string_view sCustomShapeEngine, xmlTextWriterPtr xmlWriter); +void dumpCustomShapeDataAsAttribute( + std::u16string_view sCustomShapeData, xmlTextWriterPtr xmlWriter); void dumpCustomShapeGeometryAsElement(const css::uno::Sequence< css::beans::PropertyValue>& aCustomShapeGeometry, xmlTextWriterPtr xmlWriter); -void dumpCustomShapeReplacementURLAsAttribute(const OUString& sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter); +void dumpCustomShapeReplacementURLAsAttribute(std::u16string_view sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter); // XShape.idl void dumpPositionAsAttribute(const css::awt::Point& rPoint, xmlTextWriterPtr xmlWriter); @@ -200,19 +202,19 @@ void dumpFillStyleAsAttribute(drawing::FillStyle eFillStyle, xmlTextWriterPtr xm switch(eFillStyle) { case drawing::FillStyle_NONE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "NONE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "NONE"); break; case drawing::FillStyle_SOLID: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "SOLID"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "SOLID"); break; case drawing::FillStyle_GRADIENT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "GRADIENT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "GRADIENT"); break; case drawing::FillStyle_HATCH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "HATCH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "HATCH"); break; case drawing::FillStyle_BITMAP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "BITMAP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillStyle"), "%s", "BITMAP"); break; default: break; @@ -221,17 +223,17 @@ void dumpFillStyleAsAttribute(drawing::FillStyle eFillStyle, xmlTextWriterPtr xm void dumpFillColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillColor"), "%06x", static_cast<unsigned int>(aColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillColor"), "%06x", static_cast<unsigned int>(aColor)); } void dumpFillTransparenceAsAttribute(sal_Int32 aTransparence, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparence"), "%" SAL_PRIdINT32, aTransparence); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparence"), "%" SAL_PRIdINT32, aTransparence); } -void dumpFillTransparenceGradientNameAsAttribute(const OUString& sTranspGradName, xmlTextWriterPtr xmlWriter) +void dumpFillTransparenceGradientNameAsAttribute(std::u16string_view sTranspGradName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparenceGradientName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillTransparenceGradientName"), "%s", OUStringToOString(sTranspGradName, RTL_TEXTENCODING_UTF8).getStr()); } @@ -241,118 +243,118 @@ void dumpGradientProperty(const awt::Gradient& rGradient, xmlTextWriterPtr xmlWr switch (rGradient.Style) //enum GradientStyle { case awt::GradientStyle_LINEAR: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "LINEAR"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "LINEAR"); break; case awt::GradientStyle_AXIAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "AXIAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "AXIAL"); break; case awt::GradientStyle_RADIAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RADIAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RADIAL"); break; case awt::GradientStyle_ELLIPTICAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ELLIPTICAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ELLIPTICAL"); break; case awt::GradientStyle_SQUARE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SQUARE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SQUARE"); break; case awt::GradientStyle_RECT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT"); break; default: break; } - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startColor"), "%06x", static_cast<unsigned int>(rGradient.StartColor)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endColor"), "%06x", static_cast<unsigned int>(rGradient.EndColor)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Angle)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("border"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Border)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("xOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.XOffset)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("yOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.YOffset)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StartIntensity)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.EndIntensity)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stepCount"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StepCount)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startColor"), "%06x", static_cast<unsigned int>(rGradient.StartColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endColor"), "%06x", static_cast<unsigned int>(rGradient.EndColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Angle)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("border"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.Border)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("xOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.XOffset)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("yOffset"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.YOffset)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("startIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StartIntensity)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("endIntensity"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.EndIntensity)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("stepCount"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rGradient.StepCount)); } void dumpFillTransparenceGradientAsElement(const awt::Gradient& rTranspGrad, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillTransparenceGradient" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillTransparenceGradient" )); dumpGradientProperty(rTranspGrad, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } -void dumpFillGradientNameAsAttribute(const OUString& sGradName, xmlTextWriterPtr xmlWriter) +void dumpFillGradientNameAsAttribute(std::u16string_view sGradName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillGradientName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillGradientName"), "%s", OUStringToOString(sGradName, RTL_TEXTENCODING_UTF8).getStr()); } void dumpFillGradientAsElement(const awt::Gradient& rGradient, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillGradient" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillGradient" )); dumpGradientProperty(rGradient, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpFillHatchAsElement(const drawing::Hatch& rHatch, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillHatch" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillHatch" )); switch (rHatch.Style) { case drawing::HatchStyle_SINGLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SINGLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "SINGLE"); break; case drawing::HatchStyle_DOUBLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "DOUBLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "DOUBLE"); break; case drawing::HatchStyle_TRIPLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "TRIPLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "TRIPLE"); break; default: break; } - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("color"), "%06x", static_cast<unsigned int>(rHatch.Color)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rHatch.Distance); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, rHatch.Angle); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("color"), "%06x", static_cast<unsigned int>(rHatch.Color)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rHatch.Distance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("angle"), "%" SAL_PRIdINT32, rHatch.Angle); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpFillBackgroundAsAttribute(bool bBackground, xmlTextWriterPtr xmlWriter) { if(bBackground) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBackground"), "%s", "false"); } void dumpFillBitmapAsElement(const uno::Reference<awt::XBitmap>& xBitmap, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillBitmap" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "FillBitmap" )); if (xBitmap.is()) { awt::Size const aSize = xBitmap->getSize(); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aSize.Width); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aSize.Height); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("width"), "%" SAL_PRIdINT32, aSize.Width); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("height"), "%" SAL_PRIdINT32, aSize.Height); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpFillBitmapPositionOffsetXAsAttribute(sal_Int32 aBitmapPositionOffsetX, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetX"), "%" SAL_PRIdINT32, aBitmapPositionOffsetX); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetX"), "%" SAL_PRIdINT32, aBitmapPositionOffsetX); } void dumpFillBitmapPositionOffsetYAsAttribute(sal_Int32 aBitmapPositionOffsetY, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetY"), "%" SAL_PRIdINT32, aBitmapPositionOffsetY); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapPositionOffsetY"), "%" SAL_PRIdINT32, aBitmapPositionOffsetY); } void dumpFillBitmapOffsetXAsAttribute(sal_Int32 aBitmapOffsetX, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetX"), "%" SAL_PRIdINT32, aBitmapOffsetX); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetX"), "%" SAL_PRIdINT32, aBitmapOffsetX); } void dumpFillBitmapOffsetYAsAttribute(sal_Int32 aBitmapOffsetY, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetY"), "%" SAL_PRIdINT32, aBitmapOffsetY); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapOffsetY"), "%" SAL_PRIdINT32, aBitmapOffsetY); } void dumpFillBitmapRectanglePointAsAttribute(drawing::RectanglePoint eBitmapRectanglePoint, xmlTextWriterPtr xmlWriter) @@ -360,31 +362,31 @@ void dumpFillBitmapRectanglePointAsAttribute(drawing::RectanglePoint eBitmapRect switch(eBitmapRectanglePoint) { case drawing::RectanglePoint_LEFT_TOP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_TOP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_TOP"); break; case drawing::RectanglePoint_MIDDLE_TOP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_TOP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_TOP"); break; case drawing::RectanglePoint_RIGHT_TOP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_TOP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_TOP"); break; case drawing::RectanglePoint_LEFT_MIDDLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_MIDDLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_MIDDLE"); break; case drawing::RectanglePoint_MIDDLE_MIDDLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_MIDDLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_MIDDLE"); break; case drawing::RectanglePoint_RIGHT_MIDDLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_MIDDLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_MIDDLE"); break; case drawing::RectanglePoint_LEFT_BOTTOM: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_BOTTOM"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "LEFT_BOTTOM"); break; case drawing::RectanglePoint_MIDDLE_BOTTOM: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_BOTTOM"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "MIDDLE_BOTTOM"); break; case drawing::RectanglePoint_RIGHT_BOTTOM: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_BOTTOM"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapRectanglePoint"), "%s", "RIGHT_BOTTOM"); break; default: break; @@ -394,19 +396,19 @@ void dumpFillBitmapRectanglePointAsAttribute(drawing::RectanglePoint eBitmapRect void dumpFillBitmapLogicalSizeAsAttribute(bool bBitmapLogicalSize, xmlTextWriterPtr xmlWriter) { if(bBitmapLogicalSize) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapLogicalSize"), "%s", "false"); } void dumpFillBitmapSizeXAsAttribute(sal_Int32 aBitmapSizeX, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeX"), "%" SAL_PRIdINT32, aBitmapSizeX); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeX"), "%" SAL_PRIdINT32, aBitmapSizeX); } void dumpFillBitmapSizeYAsAttribute(sal_Int32 aBitmapSizeY, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeY"), "%" SAL_PRIdINT32, aBitmapSizeY); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fillBitmapSizeY"), "%" SAL_PRIdINT32, aBitmapSizeY); } void dumpFillBitmapModeAsAttribute(drawing::BitmapMode eBitmapMode, xmlTextWriterPtr xmlWriter) @@ -414,13 +416,13 @@ void dumpFillBitmapModeAsAttribute(drawing::BitmapMode eBitmapMode, xmlTextWrite switch(eBitmapMode) { case drawing::BitmapMode_REPEAT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "REPEAT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "REPEAT"); break; case drawing::BitmapMode_STRETCH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "STRETCH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "STRETCH"); break; case drawing::BitmapMode_NO_REPEAT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "NO_REPEAT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapMode"), "%s", "NO_REPEAT"); break; default: break; @@ -430,17 +432,17 @@ void dumpFillBitmapModeAsAttribute(drawing::BitmapMode eBitmapMode, xmlTextWrite void dumpFillBitmapStretchAsAttribute(bool bBitmapStretch, xmlTextWriterPtr xmlWriter) { if(bBitmapStretch) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapStretch"), "%s", "false"); } void dumpFillBitmapTileAsAttribute(bool bBitmapTile, xmlTextWriterPtr xmlWriter) { if(bBitmapTile) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fillBitmapTile"), "%s", "false"); } @@ -452,13 +454,13 @@ void dumpLineStyleAsAttribute(drawing::LineStyle eLineStyle, xmlTextWriterPtr xm switch(eLineStyle) { case drawing::LineStyle_NONE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "NONE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "NONE"); break; case drawing::LineStyle_SOLID: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "SOLID"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "SOLID"); break; case drawing::LineStyle_DASH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "DASH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStyle"), "%s", "DASH"); break; default: break; @@ -467,51 +469,51 @@ void dumpLineStyleAsAttribute(drawing::LineStyle eLineStyle, xmlTextWriterPtr xm void dumpLineDashAsElement(const drawing::LineDash& rLineDash, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineDash" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineDash" )); switch (rLineDash.Style) { case drawing::DashStyle_RECT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECT"); break; case drawing::DashStyle_ROUND: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUND"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUND"); break; case drawing::DashStyle_RECTRELATIVE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECTRELATIVE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "RECTRELATIVE"); break; case drawing::DashStyle_ROUNDRELATIVE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUNDRELATIVE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("style"), "%s", "ROUNDRELATIVE"); break; default: break; } - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dots"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dots)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dotLen"), "%" SAL_PRIdINT32, rLineDash.DotLen); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashes"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dashes)); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashLen"), "%" SAL_PRIdINT32, rLineDash.DashLen); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rLineDash.Distance); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dots"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dots)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dotLen"), "%" SAL_PRIdINT32, rLineDash.DotLen); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashes"), "%" SAL_PRIdINT32, static_cast<sal_Int32>(rLineDash.Dashes)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("dashLen"), "%" SAL_PRIdINT32, rLineDash.DashLen); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("distance"), "%" SAL_PRIdINT32, rLineDash.Distance); + (void)xmlTextWriterEndElement( xmlWriter ); } -void dumpLineDashNameAsAttribute(const OUString& sLineDashName, xmlTextWriterPtr xmlWriter) +void dumpLineDashNameAsAttribute(std::u16string_view sLineDashName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineDashName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineDashName"), "%s", OUStringToOString(sLineDashName, RTL_TEXTENCODING_UTF8).getStr()); } void dumpLineColorAsAttribute(sal_Int32 aLineColor, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineColor"), "%06x", static_cast<unsigned int>(aLineColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineColor"), "%06x", static_cast<unsigned int>(aLineColor)); } void dumpLineTransparenceAsAttribute(sal_Int32 aLineTransparence, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineTransparence"), "%" SAL_PRIdINT32, aLineTransparence); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineTransparence"), "%" SAL_PRIdINT32, aLineTransparence); } void dumpLineWidthAsAttribute(sal_Int32 aLineWidth, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineWidth"), "%" SAL_PRIdINT32, aLineWidth); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineWidth"), "%" SAL_PRIdINT32, aLineWidth); } void dumpLineJointAsAttribute(drawing::LineJoint eLineJoint, xmlTextWriterPtr xmlWriter) @@ -519,34 +521,34 @@ void dumpLineJointAsAttribute(drawing::LineJoint eLineJoint, xmlTextWriterPtr xm switch(eLineJoint) { case drawing::LineJoint_NONE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "NONE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "NONE"); break; case drawing::LineJoint_MIDDLE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MIDDLE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MIDDLE"); break; case drawing::LineJoint_BEVEL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "BEVEL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "BEVEL"); break; case drawing::LineJoint_MITER: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MITER"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "MITER"); break; case drawing::LineJoint_ROUND: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "ROUND"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineJoint"), "%s", "ROUND"); break; default: break; } } -void dumpLineStartNameAsAttribute(const OUString& sLineStartName, xmlTextWriterPtr xmlWriter) +void dumpLineStartNameAsAttribute(std::u16string_view sLineStartName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartName"), "%s", OUStringToOString(sLineStartName, RTL_TEXTENCODING_UTF8).getStr()); } -void dumpLineEndNameAsAttribute(const OUString& sLineEndName, xmlTextWriterPtr xmlWriter) +void dumpLineEndNameAsAttribute(std::u16string_view sLineEndName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndName"), "%s", OUStringToOString(sLineEndName, RTL_TEXTENCODING_UTF8).getStr()); } @@ -557,42 +559,42 @@ void dumpPolyPolygonBezierCoords(const drawing::PolyPolygonBezierCoords& rPolyPo void dumpLineStartAsElement(const drawing::PolyPolygonBezierCoords& rLineStart, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineStart" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineStart" )); dumpPolyPolygonBezierCoords(rLineStart, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpLineEndAsElement(const drawing::PolyPolygonBezierCoords& rLineEnd, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineEnd" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "LineEnd" )); dumpPolyPolygonBezierCoords(rLineEnd, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpLineStartCenterAsAttribute(bool bLineStartCenter, xmlTextWriterPtr xmlWriter) { if(bLineStartCenter) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineStartCenter"), "%s", "false"); } void dumpLineStartWidthAsAttribute(sal_Int32 aLineStartWidth, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartWidth"), "%" SAL_PRIdINT32, aLineStartWidth); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineStartWidth"), "%" SAL_PRIdINT32, aLineStartWidth); } void dumpLineEndCenterAsAttribute(bool bLineEndCenter, xmlTextWriterPtr xmlWriter) { if(bLineEndCenter) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("lineEndCenter"), "%s", "false"); } void dumpLineEndWidthAsAttribute(sal_Int32 aLineEndWidth, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndWidth"), "%" SAL_PRIdINT32, aLineEndWidth); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("lineEndWidth"), "%" SAL_PRIdINT32, aLineEndWidth); } @@ -604,31 +606,31 @@ void dumpPolygonKindAsAttribute(drawing::PolygonKind ePolygonKind, xmlTextWriter switch(ePolygonKind) { case drawing::PolygonKind_LINE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "LINE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "LINE"); break; case drawing::PolygonKind_POLY: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "POLY"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "POLY"); break; case drawing::PolygonKind_PLIN: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PLIN"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PLIN"); break; case drawing::PolygonKind_PATHLINE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHLINE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHLINE"); break; case drawing::PolygonKind_PATHFILL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHFILL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHFILL"); break; case drawing::PolygonKind_FREELINE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREELINE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREELINE"); break; case drawing::PolygonKind_FREEFILL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREEFILL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "FREEFILL"); break; case drawing::PolygonKind_PATHPOLY: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPOLY"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPOLY"); break; case drawing::PolygonKind_PATHPLIN: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPLIN"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonKind"), "%s", "PATHPLIN"); break; default: break; @@ -638,76 +640,75 @@ void dumpPolygonKindAsAttribute(drawing::PolygonKind ePolygonKind, xmlTextWriter void dumpPointSequenceSequence(const drawing::PointSequenceSequence& aPointSequenceSequence, const uno::Sequence<uno::Sequence< drawing::PolygonFlags > >* pFlags, xmlTextWriterPtr xmlWriter) { // LibreOffice proudly presents - The Sequenception - uno::Sequence<uno::Sequence< awt::Point > > pointSequenceSequence = aPointSequenceSequence; - sal_Int32 nPointsSequence = pointSequenceSequence.getLength(); + sal_Int32 nPointsSequence = aPointSequenceSequence.getLength(); for (sal_Int32 i = 0; i < nPointsSequence; ++i) { - uno::Sequence< awt::Point > pointSequence = pointSequenceSequence[i]; + const uno::Sequence< awt::Point >& pointSequence = aPointSequenceSequence[i]; sal_Int32 nPoints = pointSequence.getLength(); uno::Sequence< drawing::PolygonFlags> flagsSequence; if(pFlags) flagsSequence = (*pFlags)[i]; - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "pointSequence" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "pointSequence" )); for(sal_Int32 j = 0; j < nPoints; ++j) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "point" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, pointSequence[j].X); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, pointSequence[j].Y); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "point" )); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, pointSequence[j].X); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, pointSequence[j].Y); if(pFlags) { switch(flagsSequence[j]) { case drawing::PolygonFlags_NORMAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "NORMAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "NORMAL"); break; case drawing::PolygonFlags_SMOOTH: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SMOOTH"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SMOOTH"); break; case drawing::PolygonFlags_CONTROL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "CONTROL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "CONTROL"); break; case drawing::PolygonFlags_SYMMETRIC: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SYMMETRIC"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("polygonFlags"), "%s", "SYMMETRIC"); break; default: break; } } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } } void dumpPolyPolygonAsElement(const drawing::PointSequenceSequence& rPolyPolygon, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PolyPolygon" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PolyPolygon" )); dumpPointSequenceSequence(rPolyPolygon, nullptr, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpGeometryAsElement(const drawing::PointSequenceSequence& aGeometry, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Geometry" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Geometry" )); dumpPointSequenceSequence(aGeometry, nullptr, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } // CharacterProperties.idl void dumpCharHeightAsAttribute(float fHeight, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fontHeight"), "%f", fHeight ); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("fontHeight"), "%f", fHeight ); } void dumpCharColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fontColor"), "%06x", static_cast<unsigned int>(aColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("fontColor"), "%06x", static_cast<unsigned int>(aColor)); } @@ -717,33 +718,33 @@ void dumpCharColorAsAttribute(sal_Int32 aColor, xmlTextWriterPtr xmlWriter) void dumpIsNumberingAsAttribute(bool bIsNumbering, xmlTextWriterPtr xmlWriter) { if(bIsNumbering) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("isNumbering"), "%s", "false"); } void dumpTextAutoGrowHeightAsAttribute(bool bTextAutoGrowHeight, xmlTextWriterPtr xmlWriter) { if(bTextAutoGrowHeight) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowHeight"), "%s", "false"); } void dumpTextAutoGrowWidthAsAttribute(bool bTextAutoGrowWidth, xmlTextWriterPtr xmlWriter) { if(bTextAutoGrowWidth) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAutoGrowWidth"), "%s", "false"); } void dumpTextContourFrameAsAttribute(bool bTextContourFrame, xmlTextWriterPtr xmlWriter) { if(bTextContourFrame) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textContourFrame"), "%s", "false"); } void dumpTextFitToSizeAsAttribute(drawing::TextFitToSizeType eTextFitToSize, xmlTextWriterPtr xmlWriter) @@ -751,16 +752,16 @@ void dumpTextFitToSizeAsAttribute(drawing::TextFitToSizeType eTextFitToSize, xml switch(eTextFitToSize) { case drawing::TextFitToSizeType_NONE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "NONE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "NONE"); break; case drawing::TextFitToSizeType_PROPORTIONAL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "PROPORTIONAL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "PROPORTIONAL"); break; case drawing::TextFitToSizeType_ALLLINES: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "ALLLINES"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "ALLLINES"); break; case drawing::TextFitToSizeType_AUTOFIT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "AUTOFIT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textFitToSize"), "%s", "AUTOFIT"); break; default: break; @@ -772,16 +773,16 @@ void dumpTextHorizontalAdjustAsAttribute(drawing::TextHorizontalAdjust eTextHori switch(eTextHorizontalAdjust) { case drawing::TextHorizontalAdjust_LEFT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "LEFT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "LEFT"); break; case drawing::TextHorizontalAdjust_CENTER: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "CENTER"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "CENTER"); break; case drawing::TextHorizontalAdjust_RIGHT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "RIGHT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "RIGHT"); break; case drawing::TextHorizontalAdjust_BLOCK: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "BLOCK"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textHorizontalAdjust"), "%s", "BLOCK"); break; default: break; @@ -793,16 +794,16 @@ void dumpTextVerticalAdjustAsAttribute(drawing::TextVerticalAdjust eTextVertical switch(eTextVerticalAdjust) { case drawing::TextVerticalAdjust_TOP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "TOP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "TOP"); break; case drawing::TextVerticalAdjust_CENTER: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "CENTER"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "CENTER"); break; case drawing::TextVerticalAdjust_BOTTOM: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BOTTOM"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BOTTOM"); break; case drawing::TextVerticalAdjust_BLOCK: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BLOCK"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textVerticalAdjust"), "%s", "BLOCK"); break; default: break; @@ -811,57 +812,57 @@ void dumpTextVerticalAdjustAsAttribute(drawing::TextVerticalAdjust eTextVertical void dumpTextLeftDistanceAsAttribute(sal_Int32 aTextLeftDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLeftDistance"), "%" SAL_PRIdINT32, aTextLeftDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLeftDistance"), "%" SAL_PRIdINT32, aTextLeftDistance); } void dumpTextRightDistanceAsAttribute(sal_Int32 aTextRightDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textRightDistance"), "%" SAL_PRIdINT32, aTextRightDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textRightDistance"), "%" SAL_PRIdINT32, aTextRightDistance); } void dumpTextUpperDistanceAsAttribute(sal_Int32 aTextUpperDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textUpperDistance"), "%" SAL_PRIdINT32, aTextUpperDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textUpperDistance"), "%" SAL_PRIdINT32, aTextUpperDistance); } void dumpTextLowerDistanceAsAttribute(sal_Int32 aTextLowerDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLowerDistance"), "%" SAL_PRIdINT32, aTextLowerDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textLowerDistance"), "%" SAL_PRIdINT32, aTextLowerDistance); } void dumpTextMaximumFrameHeightAsAttribute(sal_Int32 aTextMaximumFrameHeight, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameHeight"), "%" SAL_PRIdINT32, aTextMaximumFrameHeight); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameHeight"), "%" SAL_PRIdINT32, aTextMaximumFrameHeight); } void dumpTextMaximumFrameWidthAsAttribute(sal_Int32 aTextMaximumFrameWidth, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameWidth"), "%" SAL_PRIdINT32, aTextMaximumFrameWidth); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMaximumFrameWidth"), "%" SAL_PRIdINT32, aTextMaximumFrameWidth); } void dumpTextMinimumFrameHeightAsAttribute(sal_Int32 aTextMinimumFrameHeight, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameHeight"), "%" SAL_PRIdINT32, aTextMinimumFrameHeight); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameHeight"), "%" SAL_PRIdINT32, aTextMinimumFrameHeight); } void dumpTextMinimumFrameWidthAsAttribute(sal_Int32 aTextMinimumFrameWidth, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameWidth"), "%" SAL_PRIdINT32, aTextMinimumFrameWidth); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textMinimumFrameWidth"), "%" SAL_PRIdINT32, aTextMinimumFrameWidth); } void dumpTextAnimationAmountAsAttribute(sal_Int32 aTextAnimationAmount, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationAmount"), "%" SAL_PRIdINT32, aTextAnimationAmount); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationAmount"), "%" SAL_PRIdINT32, aTextAnimationAmount); } void dumpTextAnimationCountAsAttribute(sal_Int32 aTextAnimationCount, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationCount"), "%" SAL_PRIdINT32, aTextAnimationCount); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationCount"), "%" SAL_PRIdINT32, aTextAnimationCount); } void dumpTextAnimationDelayAsAttribute(sal_Int32 aTextAnimationDelay, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationDelay"), "%" SAL_PRIdINT32, aTextAnimationDelay); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("textAnimationDelay"), "%" SAL_PRIdINT32, aTextAnimationDelay); } void dumpTextAnimationDirectionAsAttribute(drawing::TextAnimationDirection eTextAnimationDirection, xmlTextWriterPtr xmlWriter) @@ -869,16 +870,16 @@ void dumpTextAnimationDirectionAsAttribute(drawing::TextAnimationDirection eText switch(eTextAnimationDirection) { case drawing::TextAnimationDirection_LEFT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "LEFT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "LEFT"); break; case drawing::TextAnimationDirection_RIGHT: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "RIGHT"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "RIGHT"); break; case drawing::TextAnimationDirection_UP: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "UP"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "UP"); break; case drawing::TextAnimationDirection_DOWN: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "DOWN"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationDirection"), "%s", "DOWN"); break; default: break; @@ -890,19 +891,19 @@ void dumpTextAnimationKindAsAttribute(drawing::TextAnimationKind eTextAnimationK switch(eTextAnimationKind) { case drawing::TextAnimationKind_NONE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "NONE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "NONE"); break; case drawing::TextAnimationKind_BLINK: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "BLINK"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "BLINK"); break; case drawing::TextAnimationKind_SCROLL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SCROLL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SCROLL"); break; case drawing::TextAnimationKind_ALTERNATE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "ALTERNATE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "ALTERNATE"); break; case drawing::TextAnimationKind_SLIDE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SLIDE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationKind"), "%s", "SLIDE"); break; default: break; @@ -912,17 +913,17 @@ void dumpTextAnimationKindAsAttribute(drawing::TextAnimationKind eTextAnimationK void dumpTextAnimationStartInsideAsAttribute(bool bTextAnimationStartInside, xmlTextWriterPtr xmlWriter) { if(bTextAnimationStartInside) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStartInside"), "%s", "false"); } void dumpTextAnimationStopInsideAsAttribute(bool bTextAnimationStopInside, xmlTextWriterPtr xmlWriter) { if(bTextAnimationStopInside) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textAnimationStopInside"), "%s", "false"); } void dumpTextWritingModeAsAttribute(text::WritingMode eTextWritingMode, xmlTextWriterPtr xmlWriter) @@ -930,13 +931,13 @@ void dumpTextWritingModeAsAttribute(text::WritingMode eTextWritingMode, xmlTextW switch(eTextWritingMode) { case text::WritingMode_LR_TB: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "LR_TB"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "LR_TB"); break; case text::WritingMode_RL_TB: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "RL_TB"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "RL_TB"); break; case text::WritingMode_TB_RL: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "TB_RL"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("textWritingMode"), "%s", "TB_RL"); break; default: break; @@ -950,29 +951,29 @@ void dumpTextWritingModeAsAttribute(text::WritingMode eTextWritingMode, xmlTextW void dumpShadowAsAttribute(bool bShadow, xmlTextWriterPtr xmlWriter) { if(bShadow) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("shadow"), "%s", "false"); } void dumpShadowColorAsAttribute(sal_Int32 aShadowColor, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowColor"), "%06x", static_cast<unsigned int>(aShadowColor)); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowColor"), "%06x", static_cast<unsigned int>(aShadowColor)); } void dumpShadowTransparenceAsAttribute(sal_Int32 aShadowTransparence, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowTransparence"), "%" SAL_PRIdINT32, aShadowTransparence); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowTransparence"), "%" SAL_PRIdINT32, aShadowTransparence); } void dumpShadowXDistanceAsAttribute(sal_Int32 aShadowXDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowXDistance"), "%" SAL_PRIdINT32, aShadowXDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowXDistance"), "%" SAL_PRIdINT32, aShadowXDistance); } void dumpShadowYDistanceAsAttribute(sal_Int32 aShadowYDistance, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowYDistance"), "%" SAL_PRIdINT32, aShadowYDistance); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("shadowYDistance"), "%" SAL_PRIdINT32, aShadowYDistance); } @@ -981,49 +982,49 @@ void dumpShadowYDistanceAsAttribute(sal_Int32 aShadowYDistance, xmlTextWriterPtr void dumpZOrderAsAttribute(sal_Int32 aZOrder, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("zOrder"), "%" SAL_PRIdINT32, aZOrder); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("zOrder"), "%" SAL_PRIdINT32, aZOrder); } void dumpLayerIDAsAttribute(sal_Int32 aLayerID, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerID"), "%" SAL_PRIdINT32, aLayerID); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerID"), "%" SAL_PRIdINT32, aLayerID); } -void dumpLayerNameAsAttribute(const OUString& sLayerName, xmlTextWriterPtr xmlWriter) +void dumpLayerNameAsAttribute(std::u16string_view sLayerName, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerName"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("layerName"), "%s", OUStringToOString(sLayerName, RTL_TEXTENCODING_UTF8).getStr()); } void dumpVisibleAsAttribute(bool bVisible, xmlTextWriterPtr xmlWriter) { if(bVisible) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("visible"), "%s", "false"); } void dumpPrintableAsAttribute(bool bPrintable, xmlTextWriterPtr xmlWriter) { if(bPrintable) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("printable"), "%s", "false"); } void dumpMoveProtectAsAttribute(bool bMoveProtect, xmlTextWriterPtr xmlWriter) { if(bMoveProtect) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("moveProtect"), "%s", "false"); } -void dumpNameAsAttribute(const OUString& sName, xmlTextWriterPtr xmlWriter) +void dumpNameAsAttribute(std::u16string_view sName, xmlTextWriterPtr xmlWriter) { - if(!sName.isEmpty() && !m_bNameDumped) + if(!sName.empty() && !m_bNameDumped) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr()); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr()); m_bNameDumped = true; } } @@ -1031,55 +1032,54 @@ void dumpNameAsAttribute(const OUString& sName, xmlTextWriterPtr xmlWriter) void dumpSizeProtectAsAttribute(bool bSizeProtect, xmlTextWriterPtr xmlWriter) { if(bSizeProtect) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "true"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "true"); else - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "false"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("sizeProtect"), "%s", "false"); } void dumpHomogenMatrixLine3(const drawing::HomogenMatrixLine3& rHomogenMatrixLine3, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column1"), "%f", rHomogenMatrixLine3.Column1); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column2"), "%f", rHomogenMatrixLine3.Column2); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column3"), "%f", rHomogenMatrixLine3.Column3); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column1"), "%f", rHomogenMatrixLine3.Column1); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column2"), "%f", rHomogenMatrixLine3.Column2); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("column3"), "%f", rHomogenMatrixLine3.Column3); } void dumpTransformationAsElement(const drawing::HomogenMatrix3& rTransformation, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Transformation" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Transformation" )); { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line1" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line1" )); dumpHomogenMatrixLine3(rTransformation.Line1, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line2" )); + (void)xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line2" )); dumpHomogenMatrixLine3(rTransformation.Line2, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line3" )); + (void)xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "Line3" )); dumpHomogenMatrixLine3(rTransformation.Line3, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpNavigationOrderAsAttribute(sal_Int32 aNavigationOrder, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("navigationOrder"), "%" SAL_PRIdINT32, aNavigationOrder); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("navigationOrder"), "%" SAL_PRIdINT32, aNavigationOrder); } -void dumpHyperlinkAsAttribute(const OUString& sHyperlink, xmlTextWriterPtr xmlWriter) +void dumpHyperlinkAsAttribute(std::u16string_view sHyperlink, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("hyperlink"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("hyperlink"), "%s", OUStringToOString(sHyperlink, RTL_TEXTENCODING_UTF8).getStr()); } void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aInteropGrabBag, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "InteropGrabBag" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "InteropGrabBag" )); - sal_Int32 nLength = aInteropGrabBag.getLength(); - for (sal_Int32 i = 0; i < nLength; ++i) - dumpPropertyValueAsElement(aInteropGrabBag[i], xmlWriter); + for (const auto& item: aInteropGrabBag) + dumpPropertyValueAsElement(item, xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } @@ -1088,19 +1088,19 @@ void dumpInteropGrabBagAsElement(const uno::Sequence< beans::PropertyValue>& aIn void dumpPositionAsAttribute(const awt::Point& rPoint, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, rPoint.X); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, rPoint.Y); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionX"), "%" SAL_PRIdINT32, rPoint.X); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("positionY"), "%" SAL_PRIdINT32, rPoint.Y); } void dumpSizeAsAttribute(const awt::Size& rSize, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeX"), "%" SAL_PRIdINT32, rSize.Width); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeY"), "%" SAL_PRIdINT32, rSize.Height); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeX"), "%" SAL_PRIdINT32, rSize.Width); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("sizeY"), "%" SAL_PRIdINT32, rSize.Height); } void dumpShapeDescriptorAsAttribute( const uno::Reference< drawing::XShapeDescriptor >& xDescr, xmlTextWriterPtr xmlWriter ) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("type"), "%s", OUStringToOString(xDescr->getShapeType(), RTL_TEXTENCODING_UTF8).getStr()); } @@ -1108,81 +1108,77 @@ void dumpShapeDescriptorAsAttribute( const uno::Reference< drawing::XShapeDescri // ---------- CustomShape.idl ---------- -void dumpCustomShapeEngineAsAttribute(const OUString& sCustomShapeEngine, xmlTextWriterPtr xmlWriter) +void dumpCustomShapeEngineAsAttribute(std::u16string_view sCustomShapeEngine, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeEngine"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeEngine"), "%s", OUStringToOString(sCustomShapeEngine, RTL_TEXTENCODING_UTF8).getStr()); } -void dumpCustomShapeDataAsAttribute(const OUString& sCustomShapeData, xmlTextWriterPtr xmlWriter) +void dumpCustomShapeDataAsAttribute( + std::u16string_view sCustomShapeData, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeData"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeData"), "%s", OUStringToOString(sCustomShapeData, RTL_TEXTENCODING_UTF8).getStr()); } void dumpPropertyValueAsElement(const beans::PropertyValue& rPropertyValue, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "PropertyValue" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(rPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr()); uno::Any aAny = rPropertyValue.Value; - OUString sValue; - float fValue; - sal_Int32 nValue; - bool bValue; - awt::Rectangle aRectangleValue; - uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues; - uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aCoordinates; - uno::Sequence< drawing::EnhancedCustomShapeSegment > aSegments; - uno::Sequence< beans::PropertyValue > aPropSeq; - if(aAny >>= sValue) - { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", + if(OUString sValue; aAny >>= sValue) + { + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr()); } - else if(aAny >>= nValue) + else if(sal_Int32 nValue; aAny >>= nValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%" SAL_PRIdINT32, nValue); } - else if(aAny >>= fValue) + else if(float fValue; aAny >>= fValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%f", fValue); } - else if(aAny >>= bValue) + else if(bool bValue; aAny >>= bValue) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("value"), "%s", (bValue? "true": "false")); } - else if(rPropertyValue.Name == "ViewBox" && (aAny >>= aRectangleValue)) + else if(awt::Rectangle aRectangleValue; + rPropertyValue.Name == "ViewBox" && (aAny >>= aRectangleValue)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpViewBoxAsElement(aRectangleValue); } - else if(rPropertyValue.Name == "AdjustmentValues" && (aAny >>= aAdjustmentValues)) + else if(uno::Sequence< drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentValues; + rPropertyValue.Name == "AdjustmentValues" && (aAny >>= aAdjustmentValues)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpAdjustmentValuesAsElement(aAdjustmentValues); } - else if(rPropertyValue.Name == "Coordinates" && (aAny >>= aCoordinates)) + else if(uno::Sequence< drawing::EnhancedCustomShapeParameterPair > aCoordinates; + rPropertyValue.Name == "Coordinates" && (aAny >>= aCoordinates)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpCoordinatesAsElement(aCoordinates); } - else if(rPropertyValue.Name == "Segments" && (aAny >>= aSegments)) + else if(uno::Sequence< drawing::EnhancedCustomShapeSegment > aSegments; + rPropertyValue.Name == "Segments" && (aAny >>= aSegments)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpSegmentsAsElement(aSegments); } - else if(aAny >>= aPropSeq) + else if(uno::Sequence< beans::PropertyValue > aPropSeq; aAny >>= aPropSeq) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( OUStringToOString(rPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr() )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( OUStringToOString(rPropertyValue.Name, RTL_TEXTENCODING_UTF8).getStr() )); sal_Int32 i = 0, nCount = aPropSeq.getLength(); for ( ; i < nCount; i++ ) dumpPropertyValueAsElement(aPropSeq[ i ], xmlWriter); - xmlTextWriterEndElement(xmlWriter); + (void)xmlTextWriterEndElement(xmlWriter); } // TODO: Add here dumping of XDocument for future OOX Smart-Art @@ -1190,39 +1186,39 @@ void dumpPropertyValueAsElement(const beans::PropertyValue& rPropertyValue, xmlT // TODO more, if necessary - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, rPropertyValue.Handle); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("handle"), "%" SAL_PRIdINT32, rPropertyValue.Handle); switch(rPropertyValue.State) { case beans::PropertyState_DIRECT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DIRECT_VALUE"); break; case beans::PropertyState_DEFAULT_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "DEFAULT_VALUE"); break; case beans::PropertyState_AMBIGUOUS_VALUE: - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("propertyState"), "%s", "AMBIGUOUS_VALUE"); break; default: break; } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpCustomShapeGeometryAsElement(const uno::Sequence< beans::PropertyValue>& aCustomShapeGeometry, xmlTextWriterPtr xmlWriter) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "CustomShapeGeometry" )); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "CustomShapeGeometry" )); sal_Int32 nLength = aCustomShapeGeometry.getLength(); for (sal_Int32 i = 0; i < nLength; ++i) dumpPropertyValueAsElement(aCustomShapeGeometry[i], xmlWriter); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } -void dumpCustomShapeReplacementURLAsAttribute(const OUString& sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter) +void dumpCustomShapeReplacementURLAsAttribute(std::u16string_view sCustomShapeReplacementURL, xmlTextWriterPtr xmlWriter) { - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeReplacementURL"), "%s", + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST("customShapeReplacementURL"), "%s", OUStringToOString(sCustomShapeReplacementURL, RTL_TEXTENCODING_UTF8).getStr()); } @@ -1231,157 +1227,157 @@ void dumpCustomShapeReplacementURLAsAttribute(const OUString& sCustomShapeReplac void dumpTextPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo(); - if(xInfo->hasPropertyByName("CharHeight")) + if(xInfo->hasPropertyByName(u"CharHeight"_ustr)) { - uno::Any anotherAny = xPropSet->getPropertyValue("CharHeight"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CharHeight"_ustr); float fHeight; if(anotherAny >>= fHeight) dumpCharHeightAsAttribute(fHeight, xmlWriter); } - if(xInfo->hasPropertyByName("CharColor")) + if(xInfo->hasPropertyByName(u"CharColor"_ustr)) { - uno::Any anotherAny = xPropSet->getPropertyValue("CharColor"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CharColor"_ustr); sal_Int32 aColor = sal_Int32(); if(anotherAny >>= aColor) dumpCharColorAsAttribute(aColor, xmlWriter); } // TODO - more properties from CharacterProperties.idl (similar to above) - if(xInfo->hasPropertyByName("IsNumbering")) + if(xInfo->hasPropertyByName(u"IsNumbering"_ustr)) { - uno::Any anotherAny = xPropSet->getPropertyValue("IsNumbering"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"IsNumbering"_ustr); bool bIsNumbering; if(anotherAny >>= bIsNumbering) dumpIsNumberingAsAttribute(bIsNumbering, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAutoGrowHeight"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAutoGrowHeight"_ustr); bool bTextAutoGrowHeight; if(anotherAny >>= bTextAutoGrowHeight) dumpTextAutoGrowHeightAsAttribute(bTextAutoGrowHeight, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAutoGrowWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAutoGrowWidth"_ustr); bool bTextAutoGrowWidth; if(anotherAny >>= bTextAutoGrowWidth) dumpTextAutoGrowWidthAsAttribute(bTextAutoGrowWidth, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextContourFrame"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextContourFrame"_ustr); bool bTextContourFrame; if(anotherAny >>= bTextContourFrame) dumpTextContourFrameAsAttribute(bTextContourFrame, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextFitToSize"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextFitToSize"_ustr); drawing::TextFitToSizeType eTextFitToSize; if(anotherAny >>= eTextFitToSize) dumpTextFitToSizeAsAttribute(eTextFitToSize, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextHorizontalAdjust"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextHorizontalAdjust"_ustr); drawing::TextHorizontalAdjust eTextHorizontalAdjust; if(anotherAny >>= eTextHorizontalAdjust) dumpTextHorizontalAdjustAsAttribute(eTextHorizontalAdjust, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextVerticalAdjust"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextVerticalAdjust"_ustr); drawing::TextVerticalAdjust eTextVerticalAdjust; if(anotherAny >>= eTextVerticalAdjust) dumpTextVerticalAdjustAsAttribute(eTextVerticalAdjust, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextLeftDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextLeftDistance"_ustr); sal_Int32 aTextLeftDistance = sal_Int32(); if(anotherAny >>= aTextLeftDistance) dumpTextLeftDistanceAsAttribute(aTextLeftDistance, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextRightDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextRightDistance"_ustr); sal_Int32 aTextRightDistance = sal_Int32(); if(anotherAny >>= aTextRightDistance) dumpTextRightDistanceAsAttribute(aTextRightDistance, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextUpperDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextUpperDistance"_ustr); sal_Int32 aTextUpperDistance = sal_Int32(); if(anotherAny >>= aTextUpperDistance) dumpTextUpperDistanceAsAttribute(aTextUpperDistance, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextLowerDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextLowerDistance"_ustr); sal_Int32 aTextLowerDistance = sal_Int32(); if(anotherAny >>= aTextLowerDistance) dumpTextLowerDistanceAsAttribute(aTextLowerDistance, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextMaximumFrameHeight"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextMaximumFrameHeight"_ustr); sal_Int32 aTextMaximumFrameHeight = sal_Int32(); if(anotherAny >>= aTextMaximumFrameHeight) dumpTextMaximumFrameHeightAsAttribute(aTextMaximumFrameHeight, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextMaximumFrameWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextMaximumFrameWidth"_ustr); sal_Int32 aTextMaximumFrameWidth = sal_Int32(); if(anotherAny >>= aTextMaximumFrameWidth) dumpTextMaximumFrameWidthAsAttribute(aTextMaximumFrameWidth, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextMinimumFrameHeight"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextMinimumFrameHeight"_ustr); sal_Int32 aTextMinimumFrameHeight = sal_Int32(); if(anotherAny >>= aTextMinimumFrameHeight) dumpTextMinimumFrameHeightAsAttribute(aTextMinimumFrameHeight, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextMinimumFrameWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextMinimumFrameWidth"_ustr); sal_Int32 aTextMinimumFrameWidth = sal_Int32(); if(anotherAny >>= aTextMinimumFrameWidth) dumpTextMinimumFrameWidthAsAttribute(aTextMinimumFrameWidth, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationAmount"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationAmount"_ustr); sal_Int32 aTextAnimationAmount = sal_Int32(); if(anotherAny >>= aTextAnimationAmount) dumpTextAnimationAmountAsAttribute(aTextAnimationAmount, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationCount"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationCount"_ustr); sal_Int32 aTextAnimationCount = sal_Int32(); if(anotherAny >>= aTextAnimationCount) dumpTextAnimationCountAsAttribute(aTextAnimationCount, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationDelay"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationDelay"_ustr); sal_Int32 aTextAnimationDelay = sal_Int32(); if(anotherAny >>= aTextAnimationDelay) dumpTextAnimationDelayAsAttribute(aTextAnimationDelay, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationDirection"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationDirection"_ustr); drawing::TextAnimationDirection eTextAnimationDirection; if(anotherAny >>= eTextAnimationDirection) dumpTextAnimationDirectionAsAttribute(eTextAnimationDirection, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationKind"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationKind"_ustr); drawing::TextAnimationKind eTextAnimationKind; if(anotherAny >>= eTextAnimationKind) dumpTextAnimationKindAsAttribute(eTextAnimationKind, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationStartInside"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationStartInside"_ustr); bool bTextAnimationStartInside; if(anotherAny >>= bTextAnimationStartInside) dumpTextAnimationStartInsideAsAttribute(bTextAnimationStartInside, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextAnimationStopInside"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextAnimationStopInside"_ustr); bool bTextAnimationStopInside; if(anotherAny >>= bTextAnimationStopInside) dumpTextAnimationStopInsideAsAttribute(bTextAnimationStopInside, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("TextWritingMode"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"TextWritingMode"_ustr); text::WritingMode eTextWritingMode; if(anotherAny >>= eTextWritingMode) dumpTextWritingModeAsAttribute(eTextWritingMode, xmlWriter); @@ -1391,139 +1387,139 @@ void dumpTextPropertiesService(const uno::Reference< beans::XPropertySet >& xPro void dumpFillPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { { - uno::Any anotherAny = xPropSet->getPropertyValue("FillStyle"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillStyle"_ustr); drawing::FillStyle eFillStyle; if(anotherAny >>= eFillStyle) dumpFillStyleAsAttribute(eFillStyle, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillColor"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillColor"_ustr); sal_Int32 aColor = sal_Int32(); if(anotherAny >>= aColor) dumpFillColorAsAttribute(aColor, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparence"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillTransparence"_ustr); sal_Int32 aTransparence = sal_Int32(); if(anotherAny >>= aTransparence) dumpFillTransparenceAsAttribute(aTransparence, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparenceGradientName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr); OUString sTranspGradName; if(anotherAny >>= sTranspGradName) dumpFillTransparenceGradientNameAsAttribute(sTranspGradName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillTransparenceGradient"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillTransparenceGradient"_ustr); awt::Gradient aTranspGrad; if(anotherAny >>= aTranspGrad) dumpFillTransparenceGradientAsElement(aTranspGrad, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillGradientName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillGradientName"_ustr); OUString sGradName; if(anotherAny >>= sGradName) dumpFillGradientNameAsAttribute(sGradName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillGradient"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillGradient"_ustr); awt::Gradient aGradient; if(anotherAny >>= aGradient) dumpFillGradientAsElement(aGradient, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillHatchName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillHatchName"_ustr); OUString sHatchName; if(anotherAny >>= sHatchName) dumpFillGradientNameAsAttribute(sHatchName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillHatch"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillHatch"_ustr); drawing::Hatch aHatch; if(anotherAny >>= aHatch) dumpFillHatchAsElement(aHatch, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBackground"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBackground"_ustr); bool bFillBackground; if(anotherAny >>= bFillBackground) dumpFillBackgroundAsAttribute(bFillBackground, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapName"_ustr); OUString sBitmapName; if(anotherAny >>= sBitmapName) dumpFillGradientNameAsAttribute(sBitmapName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmap"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmap"_ustr); uno::Reference<awt::XBitmap> xBitmap; if(anotherAny >>= xBitmap) dumpFillBitmapAsElement(xBitmap, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapPositionOffsetX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapPositionOffsetX"_ustr); sal_Int32 aBitmapPositionOffsetX = sal_Int32(); if(anotherAny >>= aBitmapPositionOffsetX) dumpFillBitmapPositionOffsetXAsAttribute(aBitmapPositionOffsetX, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapPositionOffsetY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapPositionOffsetY"_ustr); sal_Int32 aBitmapPositionOffsetY = sal_Int32(); if(anotherAny >>= aBitmapPositionOffsetY) dumpFillBitmapPositionOffsetYAsAttribute(aBitmapPositionOffsetY, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapOffsetX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapOffsetX"_ustr); sal_Int32 aBitmapOffsetX = sal_Int32(); if(anotherAny >>= aBitmapOffsetX) dumpFillBitmapOffsetXAsAttribute(aBitmapOffsetX, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapOffsetY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapOffsetY"_ustr); sal_Int32 aBitmapOffsetY = sal_Int32(); if(anotherAny >>= aBitmapOffsetY) dumpFillBitmapOffsetYAsAttribute(aBitmapOffsetY, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapRectanglePoint"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapRectanglePoint"_ustr); drawing::RectanglePoint eBitmapRectanglePoint; if(anotherAny >>= eBitmapRectanglePoint) dumpFillBitmapRectanglePointAsAttribute(eBitmapRectanglePoint, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapLogicalSize"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapLogicalSize"_ustr); bool bBitmapLogicalSize; if(anotherAny >>= bBitmapLogicalSize) dumpFillBitmapLogicalSizeAsAttribute(bBitmapLogicalSize, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapSizeX"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapSizeX"_ustr); sal_Int32 aBitmapSizeX = sal_Int32(); if(anotherAny >>= aBitmapSizeX) dumpFillBitmapSizeXAsAttribute(aBitmapSizeX, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapSizeY"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapSizeY"_ustr); sal_Int32 aBitmapSizeY = sal_Int32(); if(anotherAny >>= aBitmapSizeY) dumpFillBitmapSizeYAsAttribute(aBitmapSizeY, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapMode"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapMode"_ustr); drawing::BitmapMode eBitmapMode; if(anotherAny >>= eBitmapMode) dumpFillBitmapModeAsAttribute(eBitmapMode, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapStretch"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapStretch"_ustr); bool bBitmapStretch; if(anotherAny >>= bBitmapStretch) dumpFillBitmapStretchAsAttribute(bBitmapStretch, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("FillBitmapTile"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"FillBitmapTile"_ustr); bool bBitmapTile; if(anotherAny >>= bBitmapTile) dumpFillBitmapTileAsAttribute(bBitmapTile, xmlWriter); @@ -1533,91 +1529,91 @@ void dumpFillPropertiesService(const uno::Reference< beans::XPropertySet >& xPro void dumpLinePropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { { - uno::Any anotherAny = xPropSet->getPropertyValue("LineStyle"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineStyle"_ustr); drawing::LineStyle eLineStyle; if(anotherAny >>= eLineStyle) dumpLineStyleAsAttribute(eLineStyle, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineDash"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineDash"_ustr); drawing::LineDash aLineDash; if(anotherAny >>= aLineDash) dumpLineDashAsElement(aLineDash, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineDashName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineDashName"_ustr); OUString sLineDashName; if(anotherAny >>= sLineDashName) dumpLineDashNameAsAttribute(sLineDashName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineColor"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineColor"_ustr); sal_Int32 aLineColor = sal_Int32(); if(anotherAny >>= aLineColor) dumpLineColorAsAttribute(aLineColor, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineTransparence"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineTransparence"_ustr); sal_Int32 aLineTransparence = sal_Int32(); if(anotherAny >>= aLineTransparence) dumpLineTransparenceAsAttribute(aLineTransparence, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineWidth"_ustr); sal_Int32 aLineWidth = sal_Int32(); if(anotherAny >>= aLineWidth) dumpLineWidthAsAttribute(aLineWidth, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineJoint"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineJoint"_ustr); drawing::LineJoint eLineJoint; if(anotherAny >>= eLineJoint) dumpLineJointAsAttribute(eLineJoint, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineStartName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineStartName"_ustr); OUString sLineStartName; if(anotherAny >>= sLineStartName) dumpLineStartNameAsAttribute(sLineStartName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineEndName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineEndName"_ustr); OUString sLineEndName; if(anotherAny >>= sLineEndName) dumpLineEndNameAsAttribute(sLineEndName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineStart"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineStart"_ustr); drawing::PolyPolygonBezierCoords aLineStart; if(anotherAny >>= aLineStart) dumpLineStartAsElement(aLineStart, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineEnd"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineEnd"_ustr); drawing::PolyPolygonBezierCoords aLineEnd; if(anotherAny >>= aLineEnd) dumpLineEndAsElement(aLineEnd, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineStartCenter"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineStartCenter"_ustr); bool bLineStartCenter; if(anotherAny >>= bLineStartCenter) dumpLineStartCenterAsAttribute(bLineStartCenter, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineStartWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineStartWidth"_ustr); sal_Int32 aLineStartWidth = sal_Int32(); if(anotherAny >>= aLineStartWidth) dumpLineStartWidthAsAttribute(aLineStartWidth, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineEndCenter"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineEndCenter"_ustr); bool bLineEndCenter; if(anotherAny >>= bLineEndCenter) dumpLineEndCenterAsAttribute(bLineEndCenter, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LineEndWidth"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LineEndWidth"_ustr); sal_Int32 aLineEndWidth = sal_Int32(); if(anotherAny >>= aLineEndWidth) dumpLineEndWidthAsAttribute(aLineEndWidth, xmlWriter); @@ -1627,31 +1623,31 @@ void dumpLinePropertiesService(const uno::Reference< beans::XPropertySet >& xPro void dumpShadowPropertiesService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { { - uno::Any anotherAny = xPropSet->getPropertyValue("Shadow"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Shadow"_ustr); bool bShadow; if(anotherAny >>= bShadow) dumpShadowAsAttribute(bShadow, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ShadowColor"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ShadowColor"_ustr); sal_Int32 aShadowColor = sal_Int32(); if(anotherAny >>= aShadowColor) dumpShadowColorAsAttribute(aShadowColor, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ShadowTransparence"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ShadowTransparence"_ustr); sal_Int32 aShadowTransparence = sal_Int32(); if(anotherAny >>= aShadowTransparence) dumpShadowTransparenceAsAttribute(aShadowTransparence, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ShadowXDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ShadowXDistance"_ustr); sal_Int32 aShadowXDistance = sal_Int32(); if(anotherAny >>= aShadowXDistance) dumpShadowXDistanceAsAttribute(aShadowXDistance, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("ShadowYDistance"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ShadowYDistance"_ustr); sal_Int32 aShadowYDistance = sal_Int32(); if(anotherAny >>= aShadowYDistance) dumpShadowYDistanceAsAttribute(aShadowYDistance, xmlWriter); @@ -1661,19 +1657,19 @@ void dumpShadowPropertiesService(const uno::Reference< beans::XPropertySet >& xP void dumpPolyPolygonDescriptorService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { { - uno::Any anotherAny = xPropSet->getPropertyValue("PolygonKind"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"PolygonKind"_ustr); drawing::PolygonKind ePolygonKind; if(anotherAny >>= ePolygonKind) dumpPolygonKindAsAttribute(ePolygonKind, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("PolyPolygon"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"PolyPolygon"_ustr); drawing::PointSequenceSequence aPolyPolygon; if(anotherAny >>= aPolyPolygon) dumpPolyPolygonAsElement(aPolyPolygon, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Geometry"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Geometry"_ustr); drawing::PointSequenceSequence aGeometry; if(anotherAny >>= aGeometry) dumpGeometryAsElement(aGeometry, xmlWriter); @@ -1684,75 +1680,75 @@ void dumpShapeService(const uno::Reference< beans::XPropertySet >& xPropSet, xml { uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo(); { - uno::Any anotherAny = xPropSet->getPropertyValue("ZOrder"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"ZOrder"_ustr); sal_Int32 aZOrder = sal_Int32(); if(anotherAny >>= aZOrder) dumpZOrderAsAttribute(aZOrder, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LayerID"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LayerID"_ustr); sal_Int32 aLayerID = sal_Int32(); if(anotherAny >>= aLayerID) dumpLayerIDAsAttribute(aLayerID, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("LayerName"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"LayerName"_ustr); OUString sLayerName; if(anotherAny >>= sLayerName) dumpLayerNameAsAttribute(sLayerName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Visible"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Visible"_ustr); bool bVisible; if(anotherAny >>= bVisible) dumpVisibleAsAttribute(bVisible, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Printable"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Printable"_ustr); bool bPrintable; if(anotherAny >>= bPrintable) dumpPrintableAsAttribute(bPrintable, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("MoveProtect"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"MoveProtect"_ustr); bool bMoveProtect; if(anotherAny >>= bMoveProtect) dumpMoveProtectAsAttribute(bMoveProtect, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Name"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Name"_ustr); OUString sName; if(anotherAny >>= sName) dumpNameAsAttribute(sName, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("SizeProtect"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"SizeProtect"_ustr); bool bSizeProtect; if(anotherAny >>= bSizeProtect) dumpSizeProtectAsAttribute(bSizeProtect, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Transformation"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Transformation"_ustr); drawing::HomogenMatrix3 aTransformation; if(anotherAny >>= aTransformation) dumpTransformationAsElement(aTransformation, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("NavigationOrder"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"NavigationOrder"_ustr); sal_Int32 aNavigationOrder = sal_Int32(); if(anotherAny >>= aNavigationOrder) dumpNavigationOrderAsAttribute(aNavigationOrder, xmlWriter); } - if(xInfo->hasPropertyByName("Hyperlink")) + if(xInfo->hasPropertyByName(u"Hyperlink"_ustr)) { - uno::Any anotherAny = xPropSet->getPropertyValue("Hyperlink"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Hyperlink"_ustr); OUString sHyperlink; if(anotherAny >>= sHyperlink) dumpHyperlinkAsAttribute(sHyperlink, xmlWriter); } - if(xInfo->hasPropertyByName("InteropGrabBag") && bDumpInteropProperties) + if(xInfo->hasPropertyByName(u"InteropGrabBag"_ustr) && bDumpInteropProperties) { - uno::Any anotherAny = xPropSet->getPropertyValue("InteropGrabBag"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"InteropGrabBag"_ustr); uno::Sequence< beans::PropertyValue> aInteropGrabBag; if(anotherAny >>= aInteropGrabBag) dumpInteropGrabBagAsElement(aInteropGrabBag, xmlWriter); @@ -1762,19 +1758,19 @@ void dumpShapeService(const uno::Reference< beans::XPropertySet >& xPropSet, xml void dumpPolyPolygonBezierDescriptorService(const uno::Reference< beans::XPropertySet >& xPropSet, xmlTextWriterPtr xmlWriter) { { - uno::Any anotherAny = xPropSet->getPropertyValue("PolygonKind"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"PolygonKind"_ustr); drawing::PolygonKind ePolygonKind; if(anotherAny >>= ePolygonKind) dumpPolygonKindAsAttribute(ePolygonKind, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("PolyPolygonBezier"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"PolyPolygonBezier"_ustr); drawing::PolyPolygonBezierCoords aPolyPolygonBezier; if(anotherAny >>= aPolyPolygonBezier) dumpPolyPolygonBezierCoords(aPolyPolygonBezier, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("Geometry"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"Geometry"_ustr); drawing::PolyPolygonBezierCoords aGeometry; if(anotherAny >>= aGeometry) dumpPolyPolygonBezierCoords(aGeometry, xmlWriter); @@ -1785,26 +1781,26 @@ void dumpCustomShapeService(const uno::Reference< beans::XPropertySet >& xPropSe { uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo(); { - uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeEngine"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CustomShapeEngine"_ustr); OUString sCustomShapeEngine; if(anotherAny >>= sCustomShapeEngine) dumpCustomShapeEngineAsAttribute(sCustomShapeEngine, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeData"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CustomShapeData"_ustr); OUString sCustomShapeData; if(anotherAny >>= sCustomShapeData) dumpCustomShapeDataAsAttribute(sCustomShapeData, xmlWriter); } { - uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeGeometry"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CustomShapeGeometry"_ustr); uno::Sequence< beans::PropertyValue> aCustomShapeGeometry; if(anotherAny >>= aCustomShapeGeometry) dumpCustomShapeGeometryAsElement(aCustomShapeGeometry, xmlWriter); } - if(xInfo->hasPropertyByName("CustomShapeReplacementURL")) + if(xInfo->hasPropertyByName(u"CustomShapeReplacementURL"_ustr)) { - uno::Any anotherAny = xPropSet->getPropertyValue("CustomShapeReplacementURL"); + uno::Any anotherAny = xPropSet->getPropertyValue(u"CustomShapeReplacementURL"_ustr); OUString sCustomShapeReplacementURL; if(anotherAny >>= sCustomShapeReplacementURL) dumpCustomShapeReplacementURLAsAttribute(sCustomShapeReplacementURL, xmlWriter); @@ -1813,7 +1809,7 @@ void dumpCustomShapeService(const uno::Reference< beans::XPropertySet >& xPropSe void dumpXShape(const uno::Reference< drawing::XShape >& xShape, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties) { - xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShape" ) ); + (void)xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShape" ) ); uno::Reference< beans::XPropertySet > xPropSet(xShape, uno::UNO_QUERY_THROW); OUString aName; m_bNameDumped = false; @@ -1828,77 +1824,77 @@ void dumpXShape(const uno::Reference< drawing::XShape >& xShape, xmlTextWriterPt uno::Reference< lang::XServiceInfo > xServiceInfo( xShape, uno::UNO_QUERY_THROW ); uno::Reference< beans::XPropertySetInfo> xInfo = xPropSet->getPropertySetInfo(); - if(xInfo->hasPropertyByName("Name")) + if(xInfo->hasPropertyByName(u"Name"_ustr)) { - uno::Any aAny = xPropSet->getPropertyValue("Name"); + uno::Any aAny = xPropSet->getPropertyValue(u"Name"_ustr); if ((aAny >>= aName) && !aName.isEmpty()) { - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr()); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("name"), "%s", OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr()); m_bNameDumped = true; } } try { - if (xServiceInfo->supportsService("com.sun.star.drawing.Text")) + if (xServiceInfo->supportsService(u"com.sun.star.drawing.Text"_ustr)) { uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY_THROW); OUString aText = xText->getString(); if(!aText.isEmpty()) - xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("text"), "%s", OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); + (void)xmlTextWriterWriteFormatAttribute( xmlWriter, BAD_CAST("text"), "%s", OUStringToOString(aText, RTL_TEXTENCODING_UTF8).getStr()); } - if(xServiceInfo->supportsService("com.sun.star.drawing.TextProperties")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.TextProperties"_ustr)) dumpTextPropertiesService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.GroupShape")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.GroupShape"_ustr)) { uno::Reference< drawing::XShapes > xShapes(xShape, uno::UNO_QUERY_THROW); dumpXShapes(xShapes, xmlWriter, bDumpInteropProperties); } - if(xServiceInfo->supportsService("com.sun.star.drawing.FillProperties")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.FillProperties"_ustr)) dumpFillPropertiesService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.LineProperties")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.LineProperties"_ustr)) dumpLinePropertiesService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonDescriptor")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.PolyPolygonDescriptor"_ustr)) dumpPolyPolygonDescriptorService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.ShadowProperties")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.ShadowProperties"_ustr)) dumpShadowPropertiesService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.Shape")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.Shape"_ustr)) dumpShapeService(xPropSet, xmlWriter, bDumpInteropProperties); - if(xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonBezierDescriptor")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.PolyPolygonBezierDescriptor"_ustr)) dumpPolyPolygonBezierDescriptorService(xPropSet, xmlWriter); - if(xServiceInfo->supportsService("com.sun.star.drawing.CustomShape")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.CustomShape"_ustr)) dumpCustomShapeService(xPropSet, xmlWriter); // EnhancedShapeDumper used - if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeExtrusion")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.EnhancedCustomShapeExtrusion"_ustr)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpEnhancedCustomShapeExtrusionService(xPropSet); } - if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeGeometry")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.EnhancedCustomShapeGeometry"_ustr)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpEnhancedCustomShapeGeometryService(xPropSet); } - if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeHandle")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.EnhancedCustomShapeHandle"_ustr)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpEnhancedCustomShapeHandleService(xPropSet); } - if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapePath")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.EnhancedCustomShapePath"_ustr)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpEnhancedCustomShapePathService(xPropSet); } - if(xServiceInfo->supportsService("com.sun.star.drawing.EnhancedCustomShapeTextPath")) + if(xServiceInfo->supportsService(u"com.sun.star.drawing.EnhancedCustomShapeTextPath"_ustr)) { EnhancedShapeDumper enhancedDumper(xmlWriter); enhancedDumper.dumpEnhancedCustomShapeTextPathService(xPropSet); @@ -1914,18 +1910,18 @@ void dumpXShape(const uno::Reference< drawing::XShape >& xShape, xmlTextWriterPt sal_Int32 nServices = aServiceNames.getLength(); for (sal_Int32 i = 0; i < nServices; ++i) { - xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ServiceName" )); - xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST( "name" ), "%s", OUStringToOString(aServiceNames[i], RTL_TEXTENCODING_UTF8).getStr()); - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterStartElement(xmlWriter, BAD_CAST( "ServiceName" )); + (void)xmlTextWriterWriteFormatAttribute(xmlWriter, BAD_CAST( "name" ), "%s", OUStringToOString(aServiceNames[i], RTL_TEXTENCODING_UTF8).getStr()); + (void)xmlTextWriterEndElement( xmlWriter ); } #endif - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } void dumpXShapes( const uno::Reference< drawing::XShapes >& xShapes, xmlTextWriterPtr xmlWriter, bool bDumpInteropProperties ) { - xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShapes" ) ); + (void)xmlTextWriterStartElement( xmlWriter, BAD_CAST( "XShapes" ) ); uno::Reference< container::XIndexAccess > xIA( xShapes, uno::UNO_QUERY_THROW); sal_Int32 nLength = xIA->getCount(); for (sal_Int32 i = 0; i < nLength; ++i) @@ -1934,7 +1930,7 @@ void dumpXShapes( const uno::Reference< drawing::XShapes >& xShapes, xmlTextWrit dumpXShape( xShape, xmlWriter, bDumpInteropProperties ); } - xmlTextWriterEndElement( xmlWriter ); + (void)xmlTextWriterEndElement( xmlWriter ); } } //end of namespace @@ -1945,7 +1941,7 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShapes>& xPageShapes, xmlTextWriterPtr xmlWriter = xmlNewTextWriter( xmlOutBuffer ); xmlTextWriterSetIndent( xmlWriter, 1 ); - xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr ); + (void)xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr ); try { @@ -1956,10 +1952,10 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShapes>& xPageShapes, std::cout << "Exception caught in XShapeDumper: " << e.Message << std::endl; } - xmlTextWriterEndDocument( xmlWriter ); + (void)xmlTextWriterEndDocument( xmlWriter ); xmlFreeTextWriter( xmlWriter ); - return OUString::fromUtf8(aString.makeStringAndClear()); + return OUString::fromUtf8(aString); } OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes, bool bDumpInteropProperties) @@ -1969,7 +1965,7 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes, xmlTextWriterPtr xmlWriter = xmlNewTextWriter( xmlOutBuffer ); xmlTextWriterSetIndent( xmlWriter, 1 ); - xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr ); + (void)xmlTextWriterStartDocument( xmlWriter, nullptr, nullptr, nullptr ); try { @@ -1980,10 +1976,10 @@ OUString XShapeDumper::dump(const uno::Reference<drawing::XShape>& xPageShapes, std::cout << "Exception caught in XShapeDumper: " << e.Message << std::endl; } - xmlTextWriterEndDocument( xmlWriter ); + (void)xmlTextWriterEndDocument( xmlWriter ); xmlFreeTextWriter( xmlWriter ); - return OUString::fromUtf8(aString.makeStringAndClear()); + return OUString::fromUtf8(aString); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/geometry/viewinformation2d.cxx b/drawinglayer/source/geometry/viewinformation2d.cxx index 08a88b23d564..d3e6bd0371a3 100644 --- a/drawinglayer/source/geometry/viewinformation2d.cxx +++ b/drawinglayer/source/geometry/viewinformation2d.cxx @@ -25,8 +25,13 @@ #include <com/sun/star/drawing/XDrawPage.hpp> #include <com/sun/star/geometry/AffineMatrix2D.hpp> #include <com/sun/star/geometry/RealRectangle2D.hpp> -#include <rtl/instance.hxx> -#include <com/sun/star/uno/Sequence.hxx> +#include <o3tl/temporary.hxx> +#include <officecfg/Office/Common.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/rendercontext/DrawModeFlags.hxx> + +#include <atomic> +#include <utility> using namespace com::sun::star; @@ -34,12 +39,14 @@ namespace drawinglayer::geometry { namespace { -constexpr OUStringLiteral g_PropertyName_ObjectTransformation = "ObjectTransformation"; -constexpr OUStringLiteral g_PropertyName_ViewTransformation = "ViewTransformation"; -constexpr OUStringLiteral g_PropertyName_Viewport = "Viewport"; -constexpr OUStringLiteral g_PropertyName_Time = "Time"; -constexpr OUStringLiteral g_PropertyName_VisualizedPage = "VisualizedPage"; -constexpr OUStringLiteral g_PropertyName_ReducedDisplayQuality = "ReducedDisplayQuality"; +constexpr OUStringLiteral g_PropertyName_ObjectTransformation = u"ObjectTransformation"; +constexpr OUStringLiteral g_PropertyName_ViewTransformation = u"ViewTransformation"; +constexpr OUStringLiteral g_PropertyName_Viewport = u"Viewport"; +constexpr OUStringLiteral g_PropertyName_Time = u"Time"; +constexpr OUStringLiteral g_PropertyName_VisualizedPage = u"VisualizedPage"; +constexpr OUStringLiteral g_PropertyName_ReducedDisplayQuality = u"ReducedDisplayQuality"; +constexpr OUStringLiteral g_PropertyName_UseAntiAliasing = u"UseAntiAliasing"; +constexpr OUStringLiteral g_PropertyName_PixelSnapHairline = u"PixelSnapHairline"; } class ImpViewInformation2D @@ -75,185 +82,32 @@ protected: // the point in time double mfViewTime; - bool mbReducedDisplayQuality : 1; - - // the complete PropertyValue representation (if already created) - uno::Sequence<beans::PropertyValue> mxViewInformation; - - // the extra PropertyValues; not represented by ViewTransformation, - // Viewport, VisualizedPage or ViewTime - uno::Sequence<beans::PropertyValue> mxExtendedInformation; - - void impInterpretPropertyValues(const uno::Sequence<beans::PropertyValue>& rViewParameters) - { - if (!rViewParameters.hasElements()) - return; - - const sal_Int32 nCount(rViewParameters.getLength()); - sal_Int32 nExtendedInsert(0); - - // prepare extended information for filtering. Maximum size is nCount - mxExtendedInformation.realloc(nCount); - - for (sal_Int32 a(0); a < nCount; a++) - { - const beans::PropertyValue& rProp = rViewParameters[a]; - - if (rProp.Name == g_PropertyName_ReducedDisplayQuality) - { - // extra information; add to filtered information - mxExtendedInformation[nExtendedInsert++] = rProp; - - // for performance reasons, also cache content locally - bool bSalBool(false); - rProp.Value >>= bSalBool; - mbReducedDisplayQuality = bSalBool; - } - else if (rProp.Name == g_PropertyName_ObjectTransformation) - { - css::geometry::AffineMatrix2D aAffineMatrix2D; - rProp.Value >>= aAffineMatrix2D; - basegfx::unotools::homMatrixFromAffineMatrix(maObjectTransformation, - aAffineMatrix2D); - } - else if (rProp.Name == g_PropertyName_ViewTransformation) - { - css::geometry::AffineMatrix2D aAffineMatrix2D; - rProp.Value >>= aAffineMatrix2D; - basegfx::unotools::homMatrixFromAffineMatrix(maViewTransformation, aAffineMatrix2D); - } - else if (rProp.Name == g_PropertyName_Viewport) - { - css::geometry::RealRectangle2D aViewport; - rProp.Value >>= aViewport; - maViewport = basegfx::unotools::b2DRectangleFromRealRectangle2D(aViewport); - } - else if (rProp.Name == g_PropertyName_Time) - { - rProp.Value >>= mfViewTime; - } - else if (rProp.Name == g_PropertyName_VisualizedPage) - { - rProp.Value >>= mxVisualizedPage; - } - else - { - // extra information; add to filtered information - mxExtendedInformation[nExtendedInsert++] = rProp; - } - } + // color to use for automatic color + Color maAutoColor; - // extra information size is now known; realloc to final size - mxExtendedInformation.realloc(nExtendedInsert); - } + // DrawModeFlags to use, these may ask to modify the + // colors/Bitmap paint according to the flags + DrawModeFlags maDrawModeFlags; - void impFillViewInformationFromContent() - { - const bool bObjectTransformationUsed(!maObjectTransformation.isIdentity()); - const bool bViewTransformationUsed(!maViewTransformation.isIdentity()); - const bool bViewportUsed(!maViewport.isEmpty()); - const bool bTimeUsed(0.0 < mfViewTime); - const bool bVisualizedPageUsed(mxVisualizedPage.is()); - const bool bReducedDisplayQualityUsed(mbReducedDisplayQuality); - const bool bExtraInformation(mxExtendedInformation.hasElements()); - sal_uInt32 nIndex(0); - const sal_uInt32 nCount((bObjectTransformationUsed ? 1 : 0) - + (bViewTransformationUsed ? 1 : 0) + (bViewportUsed ? 1 : 0) - + (bTimeUsed ? 1 : 0) + (bVisualizedPageUsed ? 1 : 0) - + (bReducedDisplayQualityUsed ? 1 : 0) - + (bExtraInformation ? mxExtendedInformation.getLength() : 0)); - - mxViewInformation.realloc(nCount); - - if (bObjectTransformationUsed) - { - css::geometry::AffineMatrix2D aAffineMatrix2D; - basegfx::unotools::affineMatrixFromHomMatrix(aAffineMatrix2D, maObjectTransformation); - mxViewInformation[nIndex].Name = g_PropertyName_ObjectTransformation; - mxViewInformation[nIndex].Value <<= aAffineMatrix2D; - nIndex++; - } + // a hint that the View that is being painted has an active TextEdit. This + // is important for handling of TextHierarchyEditPrimitive2D to suppress + // the text for objects in TextEdit - the text is visualized by the + // active EditEngine/Outliner overlay, so it would be double visualized + bool mbTextEditActive : 1; - if (bViewTransformationUsed) - { - css::geometry::AffineMatrix2D aAffineMatrix2D; - basegfx::unotools::affineMatrixFromHomMatrix(aAffineMatrix2D, maViewTransformation); - mxViewInformation[nIndex].Name = g_PropertyName_ViewTransformation; - mxViewInformation[nIndex].Value <<= aAffineMatrix2D; - nIndex++; - } + // processed view is an EditView + bool mbEditViewActive : 1; - if (bViewportUsed) - { - const css::geometry::RealRectangle2D aViewport( - basegfx::unotools::rectangle2DFromB2DRectangle(maViewport)); - mxViewInformation[nIndex].Name = g_PropertyName_Viewport; - mxViewInformation[nIndex].Value <<= aViewport; - nIndex++; - } - - if (bTimeUsed) - { - mxViewInformation[nIndex].Name = g_PropertyName_Time; - mxViewInformation[nIndex].Value <<= mfViewTime; - nIndex++; - } + // allow to reduce DisplayQuality (e.g. sw 3d fallback renderer for interactions) + bool mbReducedDisplayQuality : 1; - if (bVisualizedPageUsed) - { - mxViewInformation[nIndex].Name = g_PropertyName_VisualizedPage; - mxViewInformation[nIndex].Value <<= mxVisualizedPage; - nIndex++; - } + // determine if to use AntiAliasing on target pixel device + bool mbUseAntiAliasing : 1; - if (bExtraInformation) - { - const sal_Int32 nExtra(mxExtendedInformation.getLength()); - - for (sal_Int32 a(0); a < nExtra; a++) - { - mxViewInformation[nIndex++] = mxExtendedInformation[a]; - } - } - } + // determine if to use PixelSnapHairline on target pixel device + bool mbPixelSnapHairline : 1; public: - ImpViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation, - const basegfx::B2DHomMatrix& rViewTransformation, - const basegfx::B2DRange& rViewport, - const uno::Reference<drawing::XDrawPage>& rxDrawPage, double fViewTime, - const uno::Sequence<beans::PropertyValue>& rExtendedParameters) - : maObjectTransformation(rObjectTransformation) - , maViewTransformation(rViewTransformation) - , maObjectToViewTransformation() - , maInverseObjectToViewTransformation() - , maViewport(rViewport) - , maDiscreteViewport() - , mxVisualizedPage(rxDrawPage) - , mfViewTime(fViewTime) - , mbReducedDisplayQuality(false) - , mxViewInformation() - , mxExtendedInformation() - { - impInterpretPropertyValues(rExtendedParameters); - } - - explicit ImpViewInformation2D(const uno::Sequence<beans::PropertyValue>& rViewParameters) - : maObjectTransformation() - , maViewTransformation() - , maObjectToViewTransformation() - , maInverseObjectToViewTransformation() - , maViewport() - , maDiscreteViewport() - , mxVisualizedPage() - , mfViewTime() - , mbReducedDisplayQuality(false) - , mxViewInformation(rViewParameters) - , mxExtendedInformation() - { - impInterpretPropertyValues(rViewParameters); - } - ImpViewInformation2D() : maObjectTransformation() , maViewTransformation() @@ -262,18 +116,45 @@ public: , maViewport() , maDiscreteViewport() , mxVisualizedPage() - , mfViewTime() + , mfViewTime(0.0) + , maAutoColor(COL_AUTO) + , maDrawModeFlags(DrawModeFlags::Default) + , mbTextEditActive(false) + , mbEditViewActive(false) , mbReducedDisplayQuality(false) - , mxViewInformation() - , mxExtendedInformation() + , mbUseAntiAliasing(ViewInformation2D::getGlobalAntiAliasing()) { + if (comphelper::IsFuzzing()) + mbPixelSnapHairline = false; + else + mbPixelSnapHairline + = mbUseAntiAliasing + && officecfg::Office::Common::Drawinglayer::SnapHorVerLinesToDiscrete::get(); } const basegfx::B2DHomMatrix& getObjectTransformation() const { return maObjectTransformation; } + void setObjectTransformation(const basegfx::B2DHomMatrix& rNew) + { + maObjectTransformation = rNew; + maObjectToViewTransformation.identity(); + maInverseObjectToViewTransformation.identity(); + } const basegfx::B2DHomMatrix& getViewTransformation() const { return maViewTransformation; } + void setViewTransformation(const basegfx::B2DHomMatrix& rNew) + { + maViewTransformation = rNew; + maDiscreteViewport.reset(); + maObjectToViewTransformation.identity(); + maInverseObjectToViewTransformation.identity(); + } const basegfx::B2DRange& getViewport() const { return maViewport; } + void setViewport(const basegfx::B2DRange& rNew) + { + maViewport = rNew; + maDiscreteViewport.reset(); + } const basegfx::B2DRange& getDiscreteViewport() const { @@ -315,64 +196,74 @@ public: } double getViewTime() const { return mfViewTime; } - - const uno::Reference<drawing::XDrawPage>& getVisualizedPage() const { return mxVisualizedPage; } - - bool getReducedDisplayQuality() const { return mbReducedDisplayQuality; } - - const uno::Sequence<beans::PropertyValue>& getViewInformationSequence() const + void setViewTime(double fNew) { - if (!mxViewInformation.hasElements()) + if (fNew >= 0.0) { - const_cast<ImpViewInformation2D*>(this)->impFillViewInformationFromContent(); + mfViewTime = fNew; } - - return mxViewInformation; } - const uno::Sequence<beans::PropertyValue>& getExtendedInformationSequence() const + const uno::Reference<drawing::XDrawPage>& getVisualizedPage() const { return mxVisualizedPage; } + void setVisualizedPage(const uno::Reference<drawing::XDrawPage>& rNew) { - return mxExtendedInformation; + mxVisualizedPage = rNew; } + Color getAutoColor() const { return maAutoColor; } + void setAutoColor(Color aNew) { maAutoColor = aNew; } + + DrawModeFlags getDrawModeFlags() const { return maDrawModeFlags; } + void setDrawModeFlags(DrawModeFlags aNew) { maDrawModeFlags = aNew; } + + bool getTextEditActive() const { return mbTextEditActive; } + void setTextEditActive(bool bNew) { mbTextEditActive = bNew; } + + bool getEditViewActive() const { return mbEditViewActive; } + void setEditViewActive(bool bNew) { mbEditViewActive = bNew; } + + bool getReducedDisplayQuality() const { return mbReducedDisplayQuality; } + void setReducedDisplayQuality(bool bNew) { mbReducedDisplayQuality = bNew; } + + bool getUseAntiAliasing() const { return mbUseAntiAliasing; } + void setUseAntiAliasing(bool bNew) { mbUseAntiAliasing = bNew; } + + bool getPixelSnapHairline() const { return mbPixelSnapHairline; } + void setPixelSnapHairline(bool bNew) { mbPixelSnapHairline = bNew; } + bool operator==(const ImpViewInformation2D& rCandidate) const { return (maObjectTransformation == rCandidate.maObjectTransformation && maViewTransformation == rCandidate.maViewTransformation && maViewport == rCandidate.maViewport && mxVisualizedPage == rCandidate.mxVisualizedPage - && mfViewTime == rCandidate.mfViewTime - && mxExtendedInformation == rCandidate.mxExtendedInformation); + && mfViewTime == rCandidate.mfViewTime && maAutoColor == rCandidate.maAutoColor + && maDrawModeFlags == rCandidate.maDrawModeFlags + && mbTextEditActive == rCandidate.mbTextEditActive + && mbEditViewActive == rCandidate.mbEditViewActive + && mbReducedDisplayQuality == rCandidate.mbReducedDisplayQuality + && mbUseAntiAliasing == rCandidate.mbUseAntiAliasing + && mbPixelSnapHairline == rCandidate.mbPixelSnapHairline); } }; namespace { -struct theGlobalDefault : public rtl::Static<ViewInformation2D::ImplType, theGlobalDefault> +ViewInformation2D::ImplType& theGlobalDefault() { -}; + static ViewInformation2D::ImplType SINGLETON; + return SINGLETON; } - -ViewInformation2D::ViewInformation2D(const basegfx::B2DHomMatrix& rObjectTransformation, - const basegfx::B2DHomMatrix& rViewTransformation, - const basegfx::B2DRange& rViewport, - const uno::Reference<drawing::XDrawPage>& rxDrawPage, - double fViewTime, - const uno::Sequence<beans::PropertyValue>& rExtendedParameters) - : mpViewInformation2D(ImpViewInformation2D(rObjectTransformation, rViewTransformation, - rViewport, rxDrawPage, fViewTime, - rExtendedParameters)) -{ -} - -ViewInformation2D::ViewInformation2D(const uno::Sequence<beans::PropertyValue>& rViewParameters) - : mpViewInformation2D(ImpViewInformation2D(rViewParameters)) -{ } ViewInformation2D::ViewInformation2D() - : mpViewInformation2D(theGlobalDefault::get()) + : mpViewInformation2D(theGlobalDefault()) { + setUseAntiAliasing(ViewInformation2D::getGlobalAntiAliasing()); + if (!comphelper::IsFuzzing()) + setPixelSnapHairline( + getUseAntiAliasing() + && officecfg::Office::Common::Drawinglayer::SnapHorVerLinesToDiscrete::get()); } ViewInformation2D::ViewInformation2D(const ViewInformation2D&) = default; @@ -395,23 +286,53 @@ const basegfx::B2DHomMatrix& ViewInformation2D::getObjectTransformation() const return mpViewInformation2D->getObjectTransformation(); } +void ViewInformation2D::setObjectTransformation(const basegfx::B2DHomMatrix& rNew) +{ + if (std::as_const(mpViewInformation2D)->getObjectTransformation() != rNew) + mpViewInformation2D->setObjectTransformation(rNew); +} + const basegfx::B2DHomMatrix& ViewInformation2D::getViewTransformation() const { return mpViewInformation2D->getViewTransformation(); } +void ViewInformation2D::setViewTransformation(const basegfx::B2DHomMatrix& rNew) +{ + if (std::as_const(mpViewInformation2D)->getViewTransformation() != rNew) + mpViewInformation2D->setViewTransformation(rNew); +} + const basegfx::B2DRange& ViewInformation2D::getViewport() const { return mpViewInformation2D->getViewport(); } +void ViewInformation2D::setViewport(const basegfx::B2DRange& rNew) +{ + if (rNew != std::as_const(mpViewInformation2D)->getViewport()) + mpViewInformation2D->setViewport(rNew); +} + double ViewInformation2D::getViewTime() const { return mpViewInformation2D->getViewTime(); } +void ViewInformation2D::setViewTime(double fNew) +{ + if (fNew != std::as_const(mpViewInformation2D)->getViewTime()) + mpViewInformation2D->setViewTime(fNew); +} + const uno::Reference<drawing::XDrawPage>& ViewInformation2D::getVisualizedPage() const { return mpViewInformation2D->getVisualizedPage(); } +void ViewInformation2D::setVisualizedPage(const uno::Reference<drawing::XDrawPage>& rNew) +{ + if (rNew != std::as_const(mpViewInformation2D)->getVisualizedPage()) + mpViewInformation2D->setVisualizedPage(rNew); +} + const basegfx::B2DHomMatrix& ViewInformation2D::getObjectToViewTransformation() const { return mpViewInformation2D->getObjectToViewTransformation(); @@ -432,14 +353,160 @@ bool ViewInformation2D::getReducedDisplayQuality() const return mpViewInformation2D->getReducedDisplayQuality(); } -const uno::Sequence<beans::PropertyValue>& ViewInformation2D::getViewInformationSequence() const +void ViewInformation2D::setReducedDisplayQuality(bool bNew) +{ + if (bNew != std::as_const(mpViewInformation2D)->getReducedDisplayQuality()) + mpViewInformation2D->setReducedDisplayQuality(bNew); +} + +bool ViewInformation2D::getUseAntiAliasing() const { - return mpViewInformation2D->getViewInformationSequence(); + return mpViewInformation2D->getUseAntiAliasing(); } -const uno::Sequence<beans::PropertyValue>& ViewInformation2D::getExtendedInformationSequence() const +void ViewInformation2D::setUseAntiAliasing(bool bNew) { - return mpViewInformation2D->getExtendedInformationSequence(); + if (bNew != std::as_const(mpViewInformation2D)->getUseAntiAliasing()) + mpViewInformation2D->setUseAntiAliasing(bNew); +} + +Color ViewInformation2D::getAutoColor() const { return mpViewInformation2D->getAutoColor(); } + +void ViewInformation2D::setAutoColor(Color aNew) { mpViewInformation2D->setAutoColor(aNew); } + +DrawModeFlags ViewInformation2D::getDrawModeFlags() const +{ + return mpViewInformation2D->getDrawModeFlags(); +} + +void ViewInformation2D::setDrawModeFlags(DrawModeFlags aNew) +{ + mpViewInformation2D->setDrawModeFlags(aNew); +} + +bool ViewInformation2D::getTextEditActive() const +{ + return mpViewInformation2D->getTextEditActive(); +} + +void ViewInformation2D::setTextEditActive(bool bNew) +{ + mpViewInformation2D->setTextEditActive(bNew); +} + +bool ViewInformation2D::getEditViewActive() const +{ + return mpViewInformation2D->getEditViewActive(); +} + +void ViewInformation2D::setEditViewActive(bool bNew) +{ + mpViewInformation2D->setEditViewActive(bNew); +} + +bool ViewInformation2D::getPixelSnapHairline() const +{ + return mpViewInformation2D->getPixelSnapHairline(); +} + +void ViewInformation2D::setPixelSnapHairline(bool bNew) +{ + if (bNew != std::as_const(mpViewInformation2D)->getPixelSnapHairline()) + mpViewInformation2D->setPixelSnapHairline(bNew); +} + +static std::atomic<bool>& globalAntiAliasing() +{ + static std::atomic<bool> g_GlobalAntiAliasing + = comphelper::IsFuzzing() || officecfg::Office::Common::Drawinglayer::AntiAliasing::get(); + return g_GlobalAntiAliasing; +} + +/** + * Some code like to turn this stuff on and off during a drawing operation + * so it can "tunnel" information down through several layers, + * so we don't want to actually do a config write all the time. + */ +void ViewInformation2D::setGlobalAntiAliasing(bool bAntiAliasing, bool bTemporary) +{ + if (globalAntiAliasing().compare_exchange_strong(o3tl::temporary(!bAntiAliasing), bAntiAliasing) + && !bTemporary) + { + auto batch = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Drawinglayer::AntiAliasing::set(bAntiAliasing, batch); + batch->commit(); + } +} +bool ViewInformation2D::getGlobalAntiAliasing() { return globalAntiAliasing(); } + +ViewInformation2D +createViewInformation2D(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters) +{ + if (!rViewParameters.hasElements()) + return ViewInformation2D(); + + ViewInformation2D aRetval; + + for (auto const& rPropertyValue : rViewParameters) + { + if (rPropertyValue.Name == g_PropertyName_ReducedDisplayQuality) + { + bool bNew(false); + rPropertyValue.Value >>= bNew; + aRetval.setReducedDisplayQuality(bNew); + } + else if (rPropertyValue.Name == g_PropertyName_PixelSnapHairline) + { + bool bNew( + true); //SvtOptionsDrawinglayer::IsAntiAliasing() && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete()); + rPropertyValue.Value >>= bNew; + aRetval.setPixelSnapHairline(bNew); + } + else if (rPropertyValue.Name == g_PropertyName_UseAntiAliasing) + { + bool bNew(true); //SvtOptionsDrawinglayer::IsAntiAliasing()); + rPropertyValue.Value >>= bNew; + aRetval.setUseAntiAliasing(bNew); + } + else if (rPropertyValue.Name == g_PropertyName_ObjectTransformation) + { + css::geometry::AffineMatrix2D aAffineMatrix2D; + rPropertyValue.Value >>= aAffineMatrix2D; + basegfx::B2DHomMatrix aTransformation; + basegfx::unotools::homMatrixFromAffineMatrix(aTransformation, aAffineMatrix2D); + aRetval.setObjectTransformation(aTransformation); + } + else if (rPropertyValue.Name == g_PropertyName_ViewTransformation) + { + css::geometry::AffineMatrix2D aAffineMatrix2D; + rPropertyValue.Value >>= aAffineMatrix2D; + basegfx::B2DHomMatrix aTransformation; + basegfx::unotools::homMatrixFromAffineMatrix(aTransformation, aAffineMatrix2D); + aRetval.setViewTransformation(aTransformation); + } + else if (rPropertyValue.Name == g_PropertyName_Viewport) + { + css::geometry::RealRectangle2D aUnoViewport; + rPropertyValue.Value >>= aUnoViewport; + const basegfx::B2DRange aViewport( + basegfx::unotools::b2DRectangleFromRealRectangle2D(aUnoViewport)); + aRetval.setViewport(aViewport); + } + else if (rPropertyValue.Name == g_PropertyName_Time) + { + double fViewTime(0.0); + rPropertyValue.Value >>= fViewTime; + aRetval.setViewTime(fViewTime); + } + else if (rPropertyValue.Name == g_PropertyName_VisualizedPage) + { + css::uno::Reference<css::drawing::XDrawPage> xVisualizedPage; + rPropertyValue.Value >>= xVisualizedPage; + aRetval.setVisualizedPage(xVisualizedPage); + } + } + + return aRetval; } } // end of namespace drawinglayer::geometry diff --git a/drawinglayer/source/geometry/viewinformation3d.cxx b/drawinglayer/source/geometry/viewinformation3d.cxx index b3e75bffa6dd..bfe601f976e8 100644 --- a/drawinglayer/source/geometry/viewinformation3d.cxx +++ b/drawinglayer/source/geometry/viewinformation3d.cxx @@ -22,8 +22,8 @@ #include <com/sun/star/beans/PropertyValue.hpp> #include <com/sun/star/geometry/AffineMatrix3D.hpp> #include <basegfx/utils/canvastools.hxx> -#include <rtl/instance.hxx> #include <com/sun/star/uno/Sequence.hxx> +#include <utility> using namespace com::sun::star; @@ -66,57 +66,19 @@ namespace drawinglayer::geometry // the point in time double mfViewTime; - // the complete PropertyValue representation (if already created) - uno::Sequence< beans::PropertyValue > mxViewInformation; - // the extra PropertyValues; does not contain the transformations uno::Sequence< beans::PropertyValue > mxExtendedInformation; // the local UNO API strings - static OUString getNamePropertyObjectTransformation() - { - return "ObjectTransformation"; - } - - static OUString getNamePropertyOrientation() - { - return "Orientation"; - } - - static OUString getNamePropertyProjection() - { - return "Projection"; - } - - static OUString getNamePropertyProjection_30() - { - return "Projection30"; - } - - static OUString getNamePropertyProjection_31() - { - return "Projection31"; - } - - static OUString getNamePropertyProjection_32() - { - return "Projection32"; - } - - static OUString getNamePropertyProjection_33() - { - return "Projection33"; - } - - static OUString getNamePropertyDeviceToView() - { - return "DeviceToView"; - } - - static OUString getNamePropertyTime() - { - return "Time"; - } + static constexpr OUString OBJECT_TRANSFORMATION = u"ObjectTransformation"_ustr; + static constexpr OUString ORIENTATION = u"Orientation"_ustr; + static constexpr OUString PROJECTION = u"Projection"_ustr; + static constexpr OUString PROJECTION30 = u"Projection30"_ustr; + static constexpr OUString PROJECTION31 = u"Projection31"_ustr; + static constexpr OUString PROJECTION32 = u"Projection32"_ustr; + static constexpr OUString PROJECTION33 = u"Projection33"_ustr; + static constexpr OUString DEVICE_TO_VIEW = u"DeviceToView"_ustr; + static constexpr OUString TIME = u"Time"_ustr; // a central PropertyValue parsing method to allow transportation of // all ViewParameters using UNO API @@ -130,24 +92,25 @@ namespace drawinglayer::geometry // prepare extended information for filtering. Maximum size is nCount mxExtendedInformation.realloc(nCount); + auto pExtendedInformation = mxExtendedInformation.getArray(); for(sal_Int32 a(0); a < nCount; a++) { const beans::PropertyValue& rProp = rViewParameters[a]; - if(rProp.Name == getNamePropertyObjectTransformation()) + if(rProp.Name == OBJECT_TRANSFORMATION) { css::geometry::AffineMatrix3D aAffineMatrix3D; rProp.Value >>= aAffineMatrix3D; maObjectTransformation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D); } - else if(rProp.Name == getNamePropertyOrientation()) + else if(rProp.Name == ORIENTATION) { css::geometry::AffineMatrix3D aAffineMatrix3D; rProp.Value >>= aAffineMatrix3D; maOrientation = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D); } - else if(rProp.Name == getNamePropertyProjection()) + else if(rProp.Name == PROJECTION) { // projection may be defined using a frustum in which case the last line of // the 4x4 matrix is not (0,0,0,1). Since AffineMatrix3D does not support that, @@ -166,44 +129,44 @@ namespace drawinglayer::geometry maProjection.set(3, 2, f_32); maProjection.set(3, 3, f_33); } - else if(rProp.Name == getNamePropertyProjection_30()) + else if(rProp.Name == PROJECTION30) { double f_30(0.0); rProp.Value >>= f_30; maProjection.set(3, 0, f_30); } - else if(rProp.Name == getNamePropertyProjection_31()) + else if(rProp.Name == PROJECTION31) { double f_31(0.0); rProp.Value >>= f_31; maProjection.set(3, 1, f_31); } - else if(rProp.Name == getNamePropertyProjection_32()) + else if(rProp.Name == PROJECTION32) { double f_32(0.0); rProp.Value >>= f_32; maProjection.set(3, 2, f_32); } - else if(rProp.Name == getNamePropertyProjection_33()) + else if(rProp.Name == PROJECTION33) { double f_33(1.0); rProp.Value >>= f_33; maProjection.set(3, 3, f_33); } - else if(rProp.Name == getNamePropertyDeviceToView()) + else if(rProp.Name == DEVICE_TO_VIEW) { css::geometry::AffineMatrix3D aAffineMatrix3D; rProp.Value >>= aAffineMatrix3D; maDeviceToView = basegfx::unotools::homMatrixFromAffineMatrix3D(aAffineMatrix3D); } - else if(rProp.Name == getNamePropertyTime()) + else if(rProp.Name == TIME) { rProp.Value >>= mfViewTime; } else { // extra information; add to filtered information - mxExtendedInformation[nExtendedInsert++] = rProp; + pExtendedInformation[nExtendedInsert++] = rProp; } } @@ -211,161 +174,31 @@ namespace drawinglayer::geometry mxExtendedInformation.realloc(nExtendedInsert); } - // central method to create a Sequence of PropertyValues containing he complete - // data set - void impFillViewInformationFromContent() - { - const bool bObjectTransformationUsed(!maObjectTransformation.isIdentity()); - const bool bOrientationUsed(!maOrientation.isIdentity()); - const bool bProjectionUsed(!maProjection.isIdentity()); - const bool bDeviceToViewUsed(!maDeviceToView.isIdentity()); - const bool bTimeUsed(0.0 < mfViewTime); - const bool bExtraInformation(mxExtendedInformation.hasElements()); - - // projection may be defined using a frustum in which case the last line of - // the 4x4 matrix is not (0,0,0,1). Since AffineMatrix3D does not support that, - // these four values need to be treated extra - const bool bProjectionUsed_30(bProjectionUsed && !basegfx::fTools::equalZero(maProjection.get(3, 0))); - const bool bProjectionUsed_31(bProjectionUsed && !basegfx::fTools::equalZero(maProjection.get(3, 1))); - const bool bProjectionUsed_32(bProjectionUsed && !basegfx::fTools::equalZero(maProjection.get(3, 2))); - const bool bProjectionUsed_33(bProjectionUsed && !basegfx::fTools::equal(maProjection.get(3, 3), 1.0)); - - sal_uInt32 nIndex(0); - const sal_uInt32 nCount( - (bObjectTransformationUsed ? 1 : 0) + - (bOrientationUsed ? 1 : 0) + - (bProjectionUsed ? 1 : 0) + - (bProjectionUsed_30 ? 1 : 0) + - (bProjectionUsed_31 ? 1 : 0) + - (bProjectionUsed_32 ? 1 : 0) + - (bProjectionUsed_33 ? 1 : 0) + - (bDeviceToViewUsed ? 1 : 0) + - (bTimeUsed ? 1 : 0) + - (bExtraInformation ? mxExtendedInformation.getLength() : 0)); - - mxViewInformation.realloc(nCount); - - if(bObjectTransformationUsed) - { - css::geometry::AffineMatrix3D aAffineMatrix3D; - basegfx::unotools::affineMatrixFromHomMatrix3D(aAffineMatrix3D, maObjectTransformation); - mxViewInformation[nIndex].Name = getNamePropertyObjectTransformation(); - mxViewInformation[nIndex].Value <<= aAffineMatrix3D; - nIndex++; - } - - if(bOrientationUsed) - { - css::geometry::AffineMatrix3D aAffineMatrix3D; - basegfx::unotools::affineMatrixFromHomMatrix3D(aAffineMatrix3D, maOrientation); - mxViewInformation[nIndex].Name = getNamePropertyOrientation(); - mxViewInformation[nIndex].Value <<= aAffineMatrix3D; - nIndex++; - } - - if(bProjectionUsed) - { - css::geometry::AffineMatrix3D aAffineMatrix3D; - basegfx::unotools::affineMatrixFromHomMatrix3D(aAffineMatrix3D, maProjection); - mxViewInformation[nIndex].Name = getNamePropertyProjection(); - mxViewInformation[nIndex].Value <<= aAffineMatrix3D; - nIndex++; - } - - if(bProjectionUsed_30) - { - mxViewInformation[nIndex].Name = getNamePropertyProjection_30(); - mxViewInformation[nIndex].Value <<= maProjection.get(3, 0); - nIndex++; - } - - if(bProjectionUsed_31) - { - mxViewInformation[nIndex].Name = getNamePropertyProjection_31(); - mxViewInformation[nIndex].Value <<= maProjection.get(3, 1); - nIndex++; - } - - if(bProjectionUsed_32) - { - mxViewInformation[nIndex].Name = getNamePropertyProjection_32(); - mxViewInformation[nIndex].Value <<= maProjection.get(3, 2); - nIndex++; - } - - if(bProjectionUsed_33) - { - mxViewInformation[nIndex].Name = getNamePropertyProjection_33(); - mxViewInformation[nIndex].Value <<= maProjection.get(3, 3); - nIndex++; - } - - if(bDeviceToViewUsed) - { - css::geometry::AffineMatrix3D aAffineMatrix3D; - basegfx::unotools::affineMatrixFromHomMatrix3D(aAffineMatrix3D, maDeviceToView); - mxViewInformation[nIndex].Name = getNamePropertyDeviceToView(); - mxViewInformation[nIndex].Value <<= aAffineMatrix3D; - nIndex++; - } - - if(bTimeUsed) - { - mxViewInformation[nIndex].Name = getNamePropertyTime(); - mxViewInformation[nIndex].Value <<= mfViewTime; - nIndex++; - } - - if(bExtraInformation) - { - const sal_Int32 nExtra(mxExtendedInformation.getLength()); - - for(sal_Int32 a(0); a < nExtra; a++) - { - mxViewInformation[nIndex++] = mxExtendedInformation[a]; - } - } - } - public: ImpViewInformation3D( - const basegfx::B3DHomMatrix& rObjectTransformation, - const basegfx::B3DHomMatrix& rOrientation, - const basegfx::B3DHomMatrix& rProjection, - const basegfx::B3DHomMatrix& rDeviceToView, + basegfx::B3DHomMatrix aObjectTransformation, + basegfx::B3DHomMatrix aOrientation, + basegfx::B3DHomMatrix aProjection, + basegfx::B3DHomMatrix aDeviceToView, double fViewTime, const uno::Sequence< beans::PropertyValue >& rExtendedParameters) - : maObjectTransformation(rObjectTransformation), - maOrientation(rOrientation), - maProjection(rProjection), - maDeviceToView(rDeviceToView), - mfViewTime(fViewTime), - mxViewInformation(), - mxExtendedInformation() + : maObjectTransformation(std::move(aObjectTransformation)), + maOrientation(std::move(aOrientation)), + maProjection(std::move(aProjection)), + maDeviceToView(std::move(aDeviceToView)), + mfViewTime(fViewTime) { impInterpretPropertyValues(rExtendedParameters); } explicit ImpViewInformation3D(const uno::Sequence< beans::PropertyValue >& rViewParameters) - : maObjectTransformation(), - maOrientation(), - maProjection(), - maDeviceToView(), - mfViewTime(), - mxViewInformation(rViewParameters), - mxExtendedInformation() + : mfViewTime() { impInterpretPropertyValues(rViewParameters); } ImpViewInformation3D() - : maObjectTransformation(), - maOrientation(), - maProjection(), - maDeviceToView(), - mfViewTime(), - mxViewInformation(), - mxExtendedInformation() + : mfViewTime() { } @@ -387,16 +220,6 @@ namespace drawinglayer::geometry return maObjectToView; } - const uno::Sequence< beans::PropertyValue >& getViewInformationSequence() const - { - if(!mxViewInformation.hasElements()) - { - const_cast< ImpViewInformation3D* >(this)->impFillViewInformationFromContent(); - } - - return mxViewInformation; - } - const uno::Sequence< beans::PropertyValue >& getExtendedInformationSequence() const { return mxExtendedInformation; @@ -419,8 +242,11 @@ namespace drawinglayer::geometry { namespace { - struct theGlobalDefault : - public rtl::Static< ViewInformation3D::ImplType, theGlobalDefault > {}; + ViewInformation3D::ImplType& theGlobalDefault() + { + static ViewInformation3D::ImplType SINGLETON; + return SINGLETON; + } } ViewInformation3D::ViewInformation3D( @@ -442,7 +268,7 @@ namespace drawinglayer::geometry } ViewInformation3D::ViewInformation3D() - : mpViewInformation3D(theGlobalDefault::get()) + : mpViewInformation3D(theGlobalDefault()) { } @@ -454,7 +280,7 @@ namespace drawinglayer::geometry bool ViewInformation3D::isDefault() const { - return mpViewInformation3D.same_object(theGlobalDefault::get()); + return mpViewInformation3D.same_object(theGlobalDefault()); } ViewInformation3D& ViewInformation3D::operator=(const ViewInformation3D&) = default; @@ -496,11 +322,6 @@ namespace drawinglayer::geometry return mpViewInformation3D->getViewTime(); } - const uno::Sequence< beans::PropertyValue >& ViewInformation3D::getViewInformationSequence() const - { - return mpViewInformation3D->getViewInformationSequence(); - } - const uno::Sequence< beans::PropertyValue >& ViewInformation3D::getExtendedInformationSequence() const { return mpViewInformation3D->getExtendedInformationSequence(); diff --git a/drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D.cxx b/drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D.cxx new file mode 100644 index 000000000000..3fe9301054e8 --- /dev/null +++ b/drawinglayer/source/primitive2d/BitmapAlphaPrimitive2D.cxx @@ -0,0 +1,105 @@ +/* -*- 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 <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <com/sun/star/awt/XBitmap.hpp> +#include <utility> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +Primitive2DReference BitmapAlphaPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (basegfx::fTools::equal(getTransparency(), 1.0)) + { + // completely transparent, done + return nullptr; + } + + if (getBitmap().IsEmpty()) + { + // no geometry, done + return nullptr; + } + + if (basegfx::fTools::equalZero(getTransparency())) + { + // no transparency, use simple BitmapPrimitive2D + return Primitive2DReference{ new BitmapPrimitive2D(getBitmap(), getTransform()) }; + } + + // default: embed to UnifiedTransparencePrimitive2D + Primitive2DContainer aContent{ new BitmapPrimitive2D(getBitmap(), getTransform()) }; + return Primitive2DReference{ new UnifiedTransparencePrimitive2D(std::move(aContent), + getTransparency()) }; +} + +BitmapAlphaPrimitive2D::BitmapAlphaPrimitive2D(Bitmap xXBitmap, basegfx::B2DHomMatrix aTransform, + double fTransparency) + : maBitmap(std::move(xXBitmap)) + , maTransform(std::move(aTransform)) + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) +{ +} + +bool BitmapAlphaPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const BitmapAlphaPrimitive2D& rCompare + = static_cast<const BitmapAlphaPrimitive2D&>(rPrimitive); + + return (getBitmap() == rCompare.getBitmap() && getTransform() == rCompare.getTransform() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); + } + + return false; +} + +basegfx::B2DRange +BitmapAlphaPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0); + aRetval.transform(maTransform); + return aRetval; +} + +sal_Int64 BitmapAlphaPrimitive2D::estimateUsage() +{ + if (getBitmap().IsEmpty()) + { + return 0; + } + return getBitmap().GetSizeBytes(); +} + +// provide unique ID +sal_uInt32 BitmapAlphaPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D; +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx new file mode 100644 index 000000000000..6a3c18b2bc44 --- /dev/null +++ b/drawinglayer/source/primitive2d/BufferedDecompositionFlusher.cxx @@ -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 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> +#include <comphelper/solarmutex.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> + +namespace drawinglayer::primitive2d +{ +/** + This is a "garbage collection" approach to flushing. + + We store entries in a set. Every 2 seconds, we scan the set for entries that have not + been used for 10 seconds or more, and if so, we flush the buffer primitives in those entries. + + This mechanism is __deliberately__ not perfect. + Sometimes things will be flushed a little too soon, sometimes things will wait a little too long, + since we only have a granularity of 2 seconds. + But what is gains from not being perfect, is scalability. + + It is very simple, scales to lots and lots of primitives without needing lots of timers, and performs + very little work in the common case. + + Shutdown notes + -------------------- + The process of handling shutdown is more complicated here than it should be, because we are interacting with + various vcl-level things (by virtue of calling into drawinglayer primitives that use vcl facilities), but we + do not have access to vcl-level API here (like SolarMutexReleaser and vcl::Timer). +*/ + +static BufferedDecompositionFlusher* getInstance() +{ + static std::unique_ptr<BufferedDecompositionFlusher> gaTimer(new BufferedDecompositionFlusher); + return gaTimer.get(); +} + +// static +void BufferedDecompositionFlusher::shutdown() +{ + BufferedDecompositionFlusher* pFlusher = getInstance(); + pFlusher->onTeardown(); + // We have to wait for the thread to exit, otherwise we might end up with the background thread + // trying to process stuff while it has things ripped out underneath it. + pFlusher->join(); +} + +// static +void BufferedDecompositionFlusher::update(const BufferedDecompositionPrimitive2D* p) +{ + getInstance()->updateImpl(p); +} + +// static +void BufferedDecompositionFlusher::update(const BufferedDecompositionGroupPrimitive2D* p) +{ + getInstance()->updateImpl(p); +} + +// static +void BufferedDecompositionFlusher::remove(const BufferedDecompositionPrimitive2D* p) +{ + getInstance()->removeImpl(p); +} + +// static +void BufferedDecompositionFlusher::remove(const BufferedDecompositionGroupPrimitive2D* p) +{ + getInstance()->removeImpl(p); +} + +BufferedDecompositionFlusher::BufferedDecompositionFlusher() { create(); } + +void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionPrimitive2D* p) +{ + std::unique_lock l(maMutex); + if (!mbShutdown) + maRegistered1.insert(const_cast<BufferedDecompositionPrimitive2D*>(p)); +} + +void BufferedDecompositionFlusher::updateImpl(const BufferedDecompositionGroupPrimitive2D* p) +{ + std::unique_lock l(maMutex); + if (!mbShutdown) + maRegistered2.insert(const_cast<BufferedDecompositionGroupPrimitive2D*>(p)); +} + +void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionPrimitive2D* p) +{ + std::unique_lock l(maMutex); + if (!mbShutdown) + maRegistered1.erase(const_cast<BufferedDecompositionPrimitive2D*>(p)); +} + +void BufferedDecompositionFlusher::removeImpl(const BufferedDecompositionGroupPrimitive2D* p) +{ + std::unique_lock l(maMutex); + if (!mbShutdown) + maRegistered2.erase(const_cast<BufferedDecompositionGroupPrimitive2D*>(p)); +} + +void SAL_CALL BufferedDecompositionFlusher::run() +{ + setName("BufferedDecompositionFlusher"); + for (;;) + { + auto aNow = std::chrono::steady_clock::now(); + std::vector<rtl::Reference<BufferedDecompositionPrimitive2D>> aRemoved1; + std::vector<rtl::Reference<BufferedDecompositionGroupPrimitive2D>> aRemoved2; + { + std::unique_lock l1(maMutex); + // exit if we have been shutdown + if (mbShutdown) + break; + for (auto it = maRegistered1.begin(); it != maRegistered1.end();) + { + if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10)) + { + aRemoved1.push_back(*it); + it = maRegistered1.erase(it); + } + else + ++it; + } + for (auto it = maRegistered2.begin(); it != maRegistered2.end();) + { + if (aNow - (*it)->maLastAccess.load() > std::chrono::seconds(10)) + { + aRemoved2.push_back(*it); + it = maRegistered2.erase(it); + } + else + ++it; + } + } + + { + // some parts of skia do not take kindly to being accessed from multiple threads + osl::Guard<comphelper::SolarMutex> aGuard(comphelper::SolarMutex::get()); + + for (const auto& r : aRemoved1) + r->setBuffered2DDecomposition(nullptr); + for (const auto& r : aRemoved2) + r->setBuffered2DDecomposition(Primitive2DContainer{}); + } + + wait(TimeValue(2, 0)); + } +} + +/// Only called by FlusherDeinit +void BufferedDecompositionFlusher::onTeardown() +{ + std::unique_lock l2(maMutex); + mbShutdown = true; + maRegistered1.clear(); + maRegistered2.clear(); +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx new file mode 100644 index 000000000000..61891a333b4b --- /dev/null +++ b/drawinglayer/source/primitive2d/BufferedDecompositionGroupPrimitive2D.cxx @@ -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 . + */ + +#include <sal/config.h> + +#include <comphelper/configuration.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionGroupPrimitive2D.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> + +namespace drawinglayer::primitive2d +{ +bool BufferedDecompositionGroupPrimitive2D::hasBuffered2DDecomposition() const +{ + if (!mbFlushOnTimer) + return !maBuffered2DDecomposition.empty(); + else + { + std::lock_guard Guard(maCallbackLock); + return !maBuffered2DDecomposition.empty(); + } +} + +void BufferedDecompositionGroupPrimitive2D::setBuffered2DDecomposition(Primitive2DContainer&& rNew) +{ + if (!mbFlushOnTimer) + { + // no flush used, just set + maBuffered2DDecomposition = std::move(rNew); + } + else + { + // decomposition changed, touch + maLastAccess = std::chrono::steady_clock::now(); + BufferedDecompositionFlusher::update(this); + + // tdf#158913 need to secure change when flush/multithreading is in use + std::lock_guard Guard(maCallbackLock); + maBuffered2DDecomposition = std::move(rNew); + } +} + +BufferedDecompositionGroupPrimitive2D::BufferedDecompositionGroupPrimitive2D( + Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) + , maCallbackLock() + , mbFlushOnTimer(false) +{ +} + +BufferedDecompositionGroupPrimitive2D::~BufferedDecompositionGroupPrimitive2D() +{ + if (mbFlushOnTimer) + BufferedDecompositionFlusher::remove(this); +} + +void BufferedDecompositionGroupPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + if (!mbFlushOnTimer) + { + // no flush/multithreading is in use, just call + if (maBuffered2DDecomposition.empty()) + create2DDecomposition(maBuffered2DDecomposition, rViewInformation); + rVisitor.visit(maBuffered2DDecomposition); + } + else + { + // tdf#158913 need to secure 'visit' when flush/multithreading is in use, + // so that the local non-ref-Counted instance of the decomposition gets not + // manipulated (e.g. deleted) + Primitive2DContainer xTmp; + { + maLastAccess = std::chrono::steady_clock::now(); + // only hold the lock for long enough to get a valid reference + std::lock_guard Guard(maCallbackLock); + if (maBuffered2DDecomposition.empty()) + { + create2DDecomposition(maBuffered2DDecomposition, rViewInformation); + BufferedDecompositionFlusher::update(this); + } + xTmp = maBuffered2DDecomposition; + } + rVisitor.visit(xTmp); + } +} + +void BufferedDecompositionGroupPrimitive2D::activateFlushOnTimer() +{ + if (comphelper::IsFuzzing()) + return; + mbFlushOnTimer = true; +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx new file mode 100644 index 000000000000..293edc32a7a6 --- /dev/null +++ b/drawinglayer/source/primitive2d/BufferedDecompositionPrimitive2D.cxx @@ -0,0 +1,113 @@ +/* -*- 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 <comphelper/configuration.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/BufferedDecompositionFlusher.hxx> + +namespace drawinglayer::primitive2d +{ +bool BufferedDecompositionPrimitive2D::hasBuffered2DDecomposition() const +{ + if (!mbFlushOnTimer) + return maBuffered2DDecomposition.is(); + else + { + std::lock_guard Guard(maCallbackLock); + return maBuffered2DDecomposition.is(); + } +} + +void BufferedDecompositionPrimitive2D::setBuffered2DDecomposition(Primitive2DReference rNew) +{ + if (!mbFlushOnTimer) + { + // no flush used, just set + maBuffered2DDecomposition = std::move(rNew); + } + else + { + // decomposition changed, touch + maLastAccess = std::chrono::steady_clock::now(); + BufferedDecompositionFlusher::update(this); + + // tdf#158913 need to secure change when flush/multithreading is in use + std::lock_guard Guard(maCallbackLock); + maBuffered2DDecomposition = std::move(rNew); + } +} + +BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() + : maBuffered2DDecomposition() + , maCallbackLock() + , mbFlushOnTimer(false) +{ +} + +BufferedDecompositionPrimitive2D::~BufferedDecompositionPrimitive2D() +{ + if (mbFlushOnTimer) + BufferedDecompositionFlusher::remove(this); +} + +void BufferedDecompositionPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + if (!mbFlushOnTimer) + { + // no flush/multithreading is in use, just call + if (!maBuffered2DDecomposition) + maBuffered2DDecomposition = create2DDecomposition(rViewInformation); + rVisitor.visit(maBuffered2DDecomposition); + } + else + { + // tdf#158913 need to secure 'visit' when flush/multithreading is in use, + // so that the local non-ref-Counted instance of the decomposition gets not + // manipulated (e.g. deleted) + Primitive2DReference xTmp; + { + maLastAccess = std::chrono::steady_clock::now(); + // only hold the lock for long enough to get a valid reference + std::lock_guard Guard(maCallbackLock); + if (!maBuffered2DDecomposition) + { + maBuffered2DDecomposition = create2DDecomposition(rViewInformation); + BufferedDecompositionFlusher::update(this); + } + xTmp = maBuffered2DDecomposition; + } + rVisitor.visit(xTmp); + } +} + +void BufferedDecompositionPrimitive2D::activateFlushOnTimer() +{ + if (comphelper::IsFuzzing()) + return; + mbFlushOnTimer = true; +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.cxx new file mode 100644 index 000000000000..0f07c2210670 --- /dev/null +++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.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 "GlowSoftEgdeShadowTools.hxx" +#include <vcl/bitmap/BitmapBasicMorphologyFilter.hxx> +#include <vcl/bitmap/BitmapFilterStackBlur.hxx> + +namespace drawinglayer::primitive2d +{ +/* Returns 8-bit alpha mask created from passed mask. + + Negative fErodeDilateRadius values mean erode, positive - dilate. + nTransparency defines minimal transparency level. +*/ +AlphaMask ProcessAndBlurAlphaMask(const AlphaMask& rMask, double fErodeDilateRadius, + double fBlurRadius, sal_uInt8 nTransparency, bool bConvertTo1Bit) +{ + // Invert it to operate in the transparency domain. Trying to update this method to + // work in the alpha domain is fraught with hazards. + AlphaMask tmpMask = rMask; + tmpMask.Invert(); + + // Only completely white pixels on the initial mask must be considered for transparency. Any + // other color must be treated as black. This creates 1-bit B&W bitmap. + Bitmap mask = bConvertTo1Bit ? tmpMask.GetBitmap().CreateMask(COL_WHITE) : tmpMask.GetBitmap(); + + // Scaling down increases performance without noticeable quality loss. Additionally, + // current blur implementation can only handle blur radius between 2 and 254. + Size aSize = mask.GetSizePixel(); + double fScale = 1.0; + while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000) + { + fScale /= 2; + fBlurRadius /= 2; + fErodeDilateRadius /= 2; + aSize /= 2; + } + + // BmpScaleFlag::NearestNeighbor is important for following color replacement + mask.Scale(fScale, fScale, BmpScaleFlag::NearestNeighbor); + + if (fErodeDilateRadius > 0) + BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius)); + else if (fErodeDilateRadius < 0) + BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF)); + + if (nTransparency) + { + const Color aTransparency(nTransparency, nTransparency, nTransparency); + mask.Replace(COL_BLACK, aTransparency); + } + + // We need 8-bit grey mask for blurring + mask.Convert(BmpConversion::N8BitGreys); + + // calculate blurry effect + BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius)); + + mask.Scale(rMask.GetSizePixel()); + + // And switch to the alpha domain. + mask.Invert(); + + return AlphaMask(mask); +} + +drawinglayer::geometry::ViewInformation2D +expandB2DRangeAtViewInformation2D(const drawinglayer::geometry::ViewInformation2D& rViewInfo, + double nAmount) +{ + drawinglayer::geometry::ViewInformation2D aRetval(rViewInfo); + basegfx::B2DRange viewport(rViewInfo.getViewport()); + viewport.grow(nAmount); + aRetval.setViewport(viewport); + return aRetval; +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx index 886ce99bb7ff..b6a62be8863e 100644 --- a/drawinglayer/source/processor2d/helperwrongspellrenderer.hxx +++ b/drawinglayer/source/primitive2d/GlowSoftEgdeShadowTools.hxx @@ -19,29 +19,24 @@ #pragma once -class OutputDevice; +#include <vcl/alpha.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> namespace drawinglayer::primitive2d { -class WrongSpellPrimitive2D; -} +/* Returns 8-bit alpha mask created from passed mask. -namespace basegfx -{ -class B2DHomMatrix; -class BColorModifierStack; -} - -// support WrongSpell rendering using VCL from primitives due to VCLs nice -// and fast solution with wavelines + Negative fErodeDilateRadius values mean erode, positive - dilate. + nTransparency defines minimal transparency level. +*/ +AlphaMask ProcessAndBlurAlphaMask(const AlphaMask& rMask, double fErodeDilateRadius, + double fBlurRadius, sal_uInt8 nTransparency, + bool bConvertTo1Bit = true); -namespace drawinglayer -{ -bool renderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate, - OutputDevice& rOutputDevice, - const basegfx::B2DHomMatrix& rObjectToViewTransformation, - const basegfx::BColorModifierStack& rBColorModifierStack); +drawinglayer::geometry::ViewInformation2D +expandB2DRangeAtViewInformation2D(const drawinglayer::geometry::ViewInformation2D& rViewInfo, + double nAmount); -} // end of namespace drawinglayer +} // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.cxx new file mode 100644 index 000000000000..63de94bb39ad --- /dev/null +++ b/drawinglayer/source/primitive2d/PolyPolygonAlphaGradientPrimitive2D.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 <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <basegfx/utils/bgradient.hxx> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +Primitive2DReference PolyPolygonAlphaGradientPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (0 == getB2DPolyPolygon().count()) + { + // no geometry, done + return nullptr; + } + + if (getAlphaGradient().isDefault()) + { + // default is a single ColorStop at 0.0 with black (0, 0, 0). The + // luminance is then 0.0, too -> not transparent at all + return new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), getBColor()); + } + + basegfx::BColor aSingleColor; + if (getAlphaGradient().getColorStops().isSingleColor(aSingleColor)) + { + // no real transparence gradient, only unified alpha, + // we can use PolyPolygonRGBAPrimitive2D + return new PolyPolygonRGBAPrimitive2D(getB2DPolyPolygon(), getBColor(), + aSingleColor.luminance()); + } + + // transparency gradient is a real gradient, create TransparencePrimitive2D + Primitive2DContainer aContent{ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), + getBColor()) }; + + Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( + basegfx::utils::getRange(getB2DPolyPolygon()), getAlphaGradient()) }; + + return new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)); +} + +PolyPolygonAlphaGradientPrimitive2D::PolyPolygonAlphaGradientPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor, + const attribute::FillGradientAttribute& rAlphaGradient) + : maPolyPolygon(rPolyPolygon) + , maBColor(rBColor) + , maAlphaGradient(rAlphaGradient) +{ +} + +bool PolyPolygonAlphaGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const PolyPolygonAlphaGradientPrimitive2D& rCompare + = static_cast<const PolyPolygonAlphaGradientPrimitive2D&>(rPrimitive); + + return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() + && getBColor() == rCompare.getBColor() + && getAlphaGradient() == rCompare.getAlphaGradient()); + } + + return false; +} + +basegfx::B2DRange PolyPolygonAlphaGradientPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // return range - without decompose + return basegfx::utils::getRange(getB2DPolyPolygon()); +} + +// provide unique ID +sal_uInt32 PolyPolygonAlphaGradientPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D; +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx index aa48a965e692..6c7cf4bb365a 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonColorPrimitive2D.cxx @@ -20,16 +20,17 @@ #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor) - : BasePrimitive2D() - , maPolyPolygon(rPolyPolygon) +PolyPolygonColorPrimitive2D::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, + const basegfx::BColor& rBColor) + : maPolyPolygon(std::move(aPolyPolygon)) , maBColor(rBColor) { } @@ -61,6 +62,54 @@ sal_uInt32 PolyPolygonColorPrimitive2D::getPrimitive2DID() const return PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D; } +FilledRectanglePrimitive2D::FilledRectanglePrimitive2D(const basegfx::B2DRange& rB2DRange, + const basegfx::BColor& rBColor) + : BasePrimitive2D() + , maB2DRange(rB2DRange) + , maBColor(rBColor) +{ +} + +bool FilledRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const FilledRectanglePrimitive2D& rCompare( + static_cast<const FilledRectanglePrimitive2D&>(rPrimitive)); + + return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor()); + } + + return false; +} + +basegfx::B2DRange FilledRectanglePrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + return getB2DRange(); +} + +sal_uInt32 FilledRectanglePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D; +} + +void FilledRectanglePrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (getB2DRange().isEmpty()) + { + // no geometry, done + return; + } + + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(getB2DRange())); + Primitive2DContainer aSequence + = { new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), getBColor()) }; + rVisitor.visit(aSequence); +} + } // end drawinglayer::primitive2d namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx index 076436b40655..d37e3d71225b 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonGradientPrimitive2D.cxx @@ -22,61 +22,83 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <rtl/ref.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonGradientPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonGradientPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { if (!getFillGradient().isDefault()) { // create SubSequence with FillGradientPrimitive2D const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange()); - FillGradientPrimitive2D* pNewGradient = new FillGradientPrimitive2D( - aPolyPolygonRange, getDefinitionRange(), getFillGradient()); - const Primitive2DReference xSubRef(pNewGradient); - const Primitive2DContainer aSubSequence{ xSubRef }; + rtl::Reference<FillGradientPrimitive2D> pNewGradient = new FillGradientPrimitive2D( + aPolyPolygonRange, getDefinitionRange(), getFillGradient(), + hasAlphaGradient() ? &getAlphaGradient() : nullptr, getTransparency()); + Primitive2DContainer aSubSequence{ pNewGradient }; // create mask primitive - rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), aSubSequence)); + return new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)); } + return nullptr; } PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D( const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::FillGradientAttribute& rFillGradient) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + : maPolyPolygon(rPolyPolygon) , maDefinitionRange(rPolyPolygon.getB2DRange()) , maFillGradient(rFillGradient) + , maAlphaGradient() + , mfTransparency(0.0) { } PolyPolygonGradientPrimitive2D::PolyPolygonGradientPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - const attribute::FillGradientAttribute& rFillGradient) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient, double fTransparency) + : maPolyPolygon(std::move(aPolyPolygon)) , maDefinitionRange(rDefinitionRange) , maFillGradient(rFillGradient) + , maAlphaGradient() + , mfTransparency(fTransparency) { + // copy alpha gradient if we got one + if (nullptr != pAlphaGradient) + maAlphaGradient = *pAlphaGradient; } bool PolyPolygonGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const PolyPolygonGradientPrimitive2D& rCompare - = static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive); + if (!(BufferedDecompositionPrimitive2D::operator==(rPrimitive))) + return false; - return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() - && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGradient() == rCompare.getFillGradient()); - } + const PolyPolygonGradientPrimitive2D& rCompare( + static_cast<const PolyPolygonGradientPrimitive2D&>(rPrimitive)); + + if (getB2DPolyPolygon() != rCompare.getB2DPolyPolygon()) + return false; + + if (getDefinitionRange() != rCompare.getDefinitionRange()) + return false; - return false; + if (getFillGradient() != rCompare.getFillGradient()) + return false; + + if (maAlphaGradient != rCompare.maAlphaGradient) + return false; + + if (!basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())) + return false; + + return true; } // provide unique ID @@ -84,7 +106,6 @@ sal_uInt32 PolyPolygonGradientPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D; } - } // end drawinglayer::primitive2d namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx index c857ba5c8e3d..fee818e23d65 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonGraphicPrimitive2D.cxx @@ -24,37 +24,53 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitivehelper2d.hxx> +#include <utility> #include <vcl/graph.hxx> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonGraphicPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonGraphicPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { + if (basegfx::fTools::equal(getTransparency(), 1.0)) + { + // completely transparent, done + return nullptr; + } + if (getFillGraphic().isDefault()) - return; + { + // no geometry data, done + return nullptr; + } - const Graphic& rGraphic = getFillGraphic().getGraphic(); + const Graphic& rGraphic(getFillGraphic().getGraphic()); const GraphicType aType(rGraphic.GetType()); // is there a bitmap or a metafile (do we have content)? if (GraphicType::Bitmap != aType && GraphicType::GdiMetafile != aType) - return; + { + // no geometry data, done + return nullptr; + } const Size aPrefSize(rGraphic.GetPrefSize()); // does content have a size? if (!(aPrefSize.Width() && aPrefSize.Height())) - return; + { + // no geometry data with size, done + return nullptr; + } // create SubSequence with FillGraphicPrimitive2D based on polygon range const basegfx::B2DRange aOutRange(getB2DPolyPolygon().getB2DRange()); - const basegfx::B2DHomMatrix aNewObjectTransform( - basegfx::utils::createScaleTranslateB2DHomMatrix(aOutRange.getRange(), - aOutRange.getMinimum())); - Primitive2DReference xSubRef; + const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix( + aOutRange.getRange(), aOutRange.getMinimum())); + drawinglayer::attribute::FillGraphicAttribute aFillGraphicAttribute(getFillGraphic()); if (aOutRange != getDefinitionRange()) { @@ -80,28 +96,27 @@ void PolyPolygonGraphicPrimitive2D::create2DDecomposition( aAdaptedRange.transform(aFromGlobalToOutRange); - const drawinglayer::attribute::FillGraphicAttribute aAdaptedFillGraphicAttribute( + aFillGraphicAttribute = drawinglayer::attribute::FillGraphicAttribute( getFillGraphic().getGraphic(), aAdaptedRange, getFillGraphic().getTiling(), getFillGraphic().getOffsetX(), getFillGraphic().getOffsetY()); - - xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, aAdaptedFillGraphicAttribute); - } - else - { - xSubRef = new FillGraphicPrimitive2D(aNewObjectTransform, getFillGraphic()); } + // use tooling due to evtl. animation info needs to be created if + // the Graphic is animated somehow + Primitive2DReference aContent( + createFillGraphicPrimitive2D(aTransform, aFillGraphicAttribute, getTransparency())); + // embed to mask primitive - rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ xSubRef })); + return new MaskPrimitive2D(getB2DPolyPolygon(), Primitive2DContainer{ aContent }); } PolyPolygonGraphicPrimitive2D::PolyPolygonGraphicPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - const attribute::FillGraphicAttribute& rFillGraphic) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::B2DRange& rDefinitionRange, + const attribute::FillGraphicAttribute& rFillGraphic, double fTransparency) + : maPolyPolygon(std::move(aPolyPolygon)) , maDefinitionRange(rDefinitionRange) , maFillGraphic(rFillGraphic) + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) { } @@ -114,7 +129,8 @@ bool PolyPolygonGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGraphic() == rCompare.getFillGraphic()); + && getFillGraphic() == rCompare.getFillGraphic() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); } return false; diff --git a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx index e4c73f9191a5..88b15160e2c0 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonHairlinePrimitive2D.cxx @@ -18,35 +18,35 @@ */ #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonHairlinePrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonHairlinePrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon()); const sal_uInt32 nCount(aPolyPolygon.count()); - if (nCount) + Primitive2DContainer aContainer; + for (sal_uInt32 a(0); a < nCount; a++) { - for (sal_uInt32 a(0); a < nCount; a++) - { - rContainer.push_back( - new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor())); - } + aContainer.push_back( + new PolygonHairlinePrimitive2D(aPolyPolygon.getB2DPolygon(a), getBColor())); } + return new GroupPrimitive2D(std::move(aContainer)); } -PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBColor) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) +PolyPolygonHairlinePrimitive2D::PolyPolygonHairlinePrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, + const basegfx::BColor& rBColor) + : maPolyPolygon(std::move(aPolyPolygon)) , maBColor(rBColor) { } diff --git a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx index ad85c02f22c1..2379e54e3de8 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonHatchPrimitive2D.cxx @@ -22,47 +22,48 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> +#include <rtl/ref.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonHatchPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonHatchPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { if (!getFillHatch().isDefault()) { // create SubSequence with FillHatchPrimitive2D const basegfx::B2DRange aPolyPolygonRange(getB2DPolyPolygon().getB2DRange()); - FillHatchPrimitive2D* pNewHatch = new FillHatchPrimitive2D( + rtl::Reference<FillHatchPrimitive2D> pNewHatch = new FillHatchPrimitive2D( aPolyPolygonRange, getDefinitionRange(), getBackgroundColor(), getFillHatch()); - const Primitive2DReference xSubRef(pNewHatch); - const Primitive2DContainer aSubSequence{ xSubRef }; + Primitive2DContainer aSubSequence{ pNewHatch }; // create mask primitive - rContainer.push_back(new MaskPrimitive2D(getB2DPolyPolygon(), aSubSequence)); + return new MaskPrimitive2D(getB2DPolyPolygon(), std::move(aSubSequence)); } + return nullptr; } PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D( const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rBackgroundColor, - const attribute::FillHatchAttribute& rFillHatch) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + attribute::FillHatchAttribute rFillHatch) + : maPolyPolygon(rPolyPolygon) , maDefinitionRange(rPolyPolygon.getB2DRange()) , maBackgroundColor(rBackgroundColor) - , maFillHatch(rFillHatch) + , maFillHatch(std::move(rFillHatch)) { } -PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rBackgroundColor, const attribute::FillHatchAttribute& rFillHatch) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) +PolyPolygonHatchPrimitive2D::PolyPolygonHatchPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, + const basegfx::B2DRange& rDefinitionRange, + const basegfx::BColor& rBackgroundColor, + attribute::FillHatchAttribute rFillHatch) + : maPolyPolygon(std::move(aPolyPolygon)) , maDefinitionRange(rDefinitionRange) , maBackgroundColor(rBackgroundColor) - , maFillHatch(rFillHatch) + , maFillHatch(std::move(rFillHatch)) { } diff --git a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx index b545ec9465fa..9c9e3b1ae81f 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonMarkerPrimitive2D.cxx @@ -18,37 +18,38 @@ */ #include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonMarkerPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonMarkerPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon()); const sal_uInt32 nCount(aPolyPolygon.count()); - if (nCount) + Primitive2DContainer aContainer; + for (sal_uInt32 a(0); a < nCount; a++) { - for (sal_uInt32 a(0); a < nCount; a++) - { - rContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a), - getRGBColorA(), getRGBColorB(), - getDiscreteDashLength())); - } + aContainer.push_back(new PolygonMarkerPrimitive2D(aPolyPolygon.getB2DPolygon(a), + getRGBColorA(), getRGBColorB(), + getDiscreteDashLength())); } + return new GroupPrimitive2D(std::move(aContainer)); } -PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rRGBColorA, - const basegfx::BColor& rRGBColorB, double fDiscreteDashLength) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) +PolyPolygonMarkerPrimitive2D::PolyPolygonMarkerPrimitive2D(basegfx::B2DPolyPolygon aPolyPolygon, + const basegfx::BColor& rRGBColorA, + const basegfx::BColor& rRGBColorB, + double fDiscreteDashLength) + : maPolyPolygon(std::move(aPolyPolygon)) , maRGBColorA(rRGBColorA) , maRGBColorB(rRGBColorB) , mfDiscreteDashLength(fDiscreteDashLength) diff --git a/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx new file mode 100644 index 000000000000..fb230af8712e --- /dev/null +++ b/drawinglayer/source/primitive2d/PolyPolygonRGBAPrimitive2D.cxx @@ -0,0 +1,99 @@ +/* -*- 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 <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +Primitive2DReference PolyPolygonRGBAPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (basegfx::fTools::equal(getTransparency(), 1.0)) + { + // completely transparent, done + return nullptr; + } + + if (0 == getB2DPolyPolygon().count()) + { + // no geometry, done + return nullptr; + } + + if (basegfx::fTools::equalZero(getTransparency())) + { + // no transparency, use simple PolyPolygonColorPrimitive2D + return Primitive2DReference{ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), + getBColor()) }; + } + + // default: embed to UnifiedTransparencePrimitive2D + Primitive2DContainer aContent{ new PolyPolygonColorPrimitive2D(getB2DPolyPolygon(), + getBColor()) }; + return Primitive2DReference{ new UnifiedTransparencePrimitive2D(std::move(aContent), + getTransparency()) }; +} + +PolyPolygonRGBAPrimitive2D::PolyPolygonRGBAPrimitive2D(const basegfx::B2DPolyPolygon& rPolyPolygon, + const basegfx::BColor& rBColor, + double fTransparency) + : maPolyPolygon(rPolyPolygon) + , maBColor(rBColor) + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) +{ +} + +bool PolyPolygonRGBAPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const PolyPolygonRGBAPrimitive2D& rCompare + = static_cast<const PolyPolygonRGBAPrimitive2D&>(rPrimitive); + + return (getB2DPolyPolygon() == rCompare.getB2DPolyPolygon() + && getBColor() == rCompare.getBColor() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); + } + + return false; +} + +basegfx::B2DRange PolyPolygonRGBAPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // return range - without decompose + return basegfx::utils::getRange(getB2DPolyPolygon()); +} + +// provide unique ID +sal_uInt32 PolyPolygonRGBAPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYPOLYGONRGBAPRIMITIVE2D; +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx index 7be684c2f71e..a5595a4bcaeb 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonSelectionPrimitive2D.cxx @@ -25,16 +25,17 @@ #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonSelectionPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonSelectionPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { if (getTransparence() >= 1.0 || !getB2DPolyPolygon().count()) - return; + return nullptr; Primitive2DContainer aRetval; @@ -61,19 +62,18 @@ void PolyPolygonSelectionPrimitive2D::create2DDecomposition( if (!aRetval.empty() && getTransparence() > 0.0) { const Primitive2DReference aTrans( - new UnifiedTransparencePrimitive2D(aRetval, getTransparence())); + new UnifiedTransparencePrimitive2D(std::move(aRetval), getTransparence())); aRetval = Primitive2DContainer{ aTrans }; } - rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); + return new GroupPrimitive2D(std::move(aRetval)); } PolyPolygonSelectionPrimitive2D::PolyPolygonSelectionPrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rColor, - double fTransparence, double fDiscreteGrow, bool bFill) - : DiscreteMetricDependentPrimitive2D() - , maPolyPolygon(rPolyPolygon) + basegfx::B2DPolyPolygon aPolyPolygon, const basegfx::BColor& rColor, double fTransparence, + double fDiscreteGrow, bool bFill) + : maPolyPolygon(std::move(aPolyPolygon)) , maColor(rColor) , mfTransparence(fTransparence) , mfDiscreteGrow(fabs(fDiscreteGrow)) diff --git a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx index 53abec1138c3..9c32d8853d8d 100644 --- a/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx +++ b/drawinglayer/source/primitive2d/PolyPolygonStrokePrimitive2D.cxx @@ -21,44 +21,42 @@ #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -void PolyPolygonStrokePrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +Primitive2DReference PolyPolygonStrokePrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { const basegfx::B2DPolyPolygon aPolyPolygon(getB2DPolyPolygon()); const sal_uInt32 nCount(aPolyPolygon.count()); - if (nCount) + Primitive2DContainer aContainer; + for (sal_uInt32 a(0); a < nCount; a++) { - for (sal_uInt32 a(0); a < nCount; a++) - { - rContainer.push_back(new PolygonStrokePrimitive2D( - aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute())); - } + aContainer.push_back(new PolygonStrokePrimitive2D( + aPolyPolygon.getB2DPolygon(a), getLineAttribute(), getStrokeAttribute())); } + return new GroupPrimitive2D(std::move(aContainer)); } PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute, - const attribute::StrokeAttribute& rStrokeAttribute) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + basegfx::B2DPolyPolygon aPolyPolygon, const attribute::LineAttribute& rLineAttribute, + attribute::StrokeAttribute aStrokeAttribute) + : maPolyPolygon(std::move(aPolyPolygon)) , maLineAttribute(rLineAttribute) - , maStrokeAttribute(rStrokeAttribute) + , maStrokeAttribute(std::move(aStrokeAttribute)) { } PolyPolygonStrokePrimitive2D::PolyPolygonStrokePrimitive2D( - const basegfx::B2DPolyPolygon& rPolyPolygon, const attribute::LineAttribute& rLineAttribute) - : BufferedDecompositionPrimitive2D() - , maPolyPolygon(rPolyPolygon) + basegfx::B2DPolyPolygon aPolyPolygon, const attribute::LineAttribute& rLineAttribute) + : maPolyPolygon(std::move(aPolyPolygon)) , maLineAttribute(rLineAttribute) - , maStrokeAttribute() { } diff --git a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx index 3df6dd791528..48b0c625e1ba 100644 --- a/drawinglayer/source/primitive2d/Primitive2DContainer.cxx +++ b/drawinglayer/source/primitive2d/Primitive2DContainer.cxx @@ -20,33 +20,45 @@ #include <sal/config.h> #include <drawinglayer/primitive2d/Primitive2DContainer.hxx> -#include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <drawinglayer/primitive2d/Tools.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> -#include <basegfx/utils/canvastools.hxx> using namespace css; namespace drawinglayer::primitive2d { -Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert) const +Primitive2DContainer::Primitive2DContainer( + const css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource) { - const sal_uInt32 nSize(size()); - Primitive2DContainer aRetval; - - aRetval.resize(nSize); + for (const auto& rPrimitive : rSource) + append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D()); +} +Primitive2DContainer::Primitive2DContainer( + const std::deque<css::uno::Reference<css::graphic::XPrimitive2D>>& rSource) +{ + for (const auto& rPrimitive : rSource) + append(static_cast<const UnoPrimitive2D*>(rPrimitive.get())->getBasePrimitive2D()); +} - for (sal_uInt32 a(0); a < nSize; a++) +css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>> +Primitive2DContainer::toSequence() const +{ + css::uno::Sequence<css::uno::Reference<css::graphic::XPrimitive2D>> aVal(size()); + auto p = aVal.getArray(); + for (const auto& rPrimitive : *this) { - aRetval[bInvert ? nSize - 1 - a : a] = (*this)[a]; + *p = new UnoPrimitive2D(rPrimitive); + ++p; } + return aVal; +} - // all entries taken over to Uno References as owners. To avoid - // errors with users of this mechanism to delete pointers to BasePrimitive2D - // itself, clear given vector - const_cast<Primitive2DContainer&>(*this).clear(); - - return aRetval; +Primitive2DContainer Primitive2DContainer::maybeInvert(bool bInvert) +{ + if (bInvert) + std::reverse(begin(), end()); + return std::move(*this); } // get B2DRange from a given Primitive2DSequence @@ -115,9 +127,27 @@ void Primitive2DContainer::append(Primitive2DContainer&& rSource) std::make_move_iterator(rSource.end())); } -void Primitive2DContainer::append(const Primitive2DSequence& rSource) +UnoPrimitive2D::~UnoPrimitive2D() {} + +css::uno::Sequence<::css::uno::Reference<::css::graphic::XPrimitive2D>> + SAL_CALL UnoPrimitive2D::getDecomposition( + const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters) +{ + std::unique_lock aGuard(m_aMutex); + return mxPrimitive->getDecomposition(rViewParameters).toSequence(); +} + +css::geometry::RealRectangle2D SAL_CALL +UnoPrimitive2D::getRange(const css::uno::Sequence<css::beans::PropertyValue>& rViewParameters) +{ + std::unique_lock aGuard(m_aMutex); + return mxPrimitive->getRange(rViewParameters); +} + +sal_Int64 SAL_CALL UnoPrimitive2D::estimateUsage() { - this->insert(this->end(), rSource.begin(), rSource.end()); + std::unique_lock aGuard(m_aMutex); + return mxPrimitive->estimateUsage(); } } // end of namespace drawinglayer::primitive2d diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx index 7db3a94c8d04..e1c3dfe96e80 100644 --- a/drawinglayer/source/primitive2d/Tools.cxx +++ b/drawinglayer/source/primitive2d/Tools.cxx @@ -21,7 +21,6 @@ #include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> -#include <basegfx/utils/canvastools.hxx> using namespace css; @@ -32,29 +31,10 @@ basegfx::B2DRange getB2DRangeFromPrimitive2DReference(const Primitive2DReference& rCandidate, const geometry::ViewInformation2D& aViewInformation) { - basegfx::B2DRange aRetval; + if (!rCandidate) + return basegfx::B2DRange(); - if (rCandidate.is()) - { - // try to get C++ implementation base - const BasePrimitive2D* pCandidate(dynamic_cast<BasePrimitive2D*>(rCandidate.get())); - - if (pCandidate) - { - // use it if possible - aRetval.expand(pCandidate->getB2DRange(aViewInformation)); - } - else - { - // use UNO API call instead - const uno::Sequence<beans::PropertyValue>& rViewParameters( - aViewInformation.getViewInformationSequence()); - aRetval.expand(basegfx::unotools::b2DRectangleFromRealRectangle2D( - rCandidate->getRange(rViewParameters))); - } - } - - return aRetval; + return rCandidate->getB2DRange(aViewInformation); } bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primitive2DReference& rxB) @@ -71,15 +51,28 @@ bool arePrimitive2DReferencesEqual(const Primitive2DReference& rxA, const Primit return true; } - const BasePrimitive2D* pA(dynamic_cast<const BasePrimitive2D*>(rxA.get())); - const BasePrimitive2D* pB(dynamic_cast<const BasePrimitive2D*>(rxB.get())); + return rxA->operator==(*rxB); +} + +bool arePrimitive2DReferencesEqual(const css::uno::Reference<css::graphic::XPrimitive2D>& rxA, + const css::uno::Reference<css::graphic::XPrimitive2D>& rxB) +{ + const bool bAIs(rxA.is()); - if (!pA || !pB) + if (bAIs != rxB.is()) { return false; } - return pA->operator==(*pB); + if (!bAIs) + { + return true; + } + + auto pA = static_cast<const UnoPrimitive2D*>(rxA.get()); + auto pB = static_cast<const UnoPrimitive2D*>(rxB.get()); + + return (*pA->getBasePrimitive2D()) == (*pB->getBasePrimitive2D()); } OUString idToString(sal_uInt32 nId) @@ -87,151 +80,169 @@ OUString idToString(sal_uInt32 nId) switch (nId) { case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: - return "TRANSPARENCE"; + return u"TRANSPARENCE"_ustr; case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D: - return "ANIMATEDSWITCH"; + return u"ANIMATEDSWITCH"_ustr; case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D: - return "ANIMATEDBLINK"; + return u"ANIMATEDBLINK"_ustr; case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D: - return "ANIMATEDINTERPOLATE"; + return u"ANIMATEDINTERPOLATE"_ustr; case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: - return "BACKGROUNDCOLOR"; + return u"BACKGROUNDCOLOR"_ustr; case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: - return "BITMAP"; + return u"BITMAP"_ustr; case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: - return "CONTROL"; + return u"CONTROL"_ustr; case PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D: - return "EMBEDDED3D"; + return u"EMBEDDED3D"_ustr; case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: - return "FILLGRAPHIC"; + return u"FILLGRAPHIC"_ustr; case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D: - return "FILLGRADIENT"; + return u"FILLGRADIENT"_ustr; case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D: - return "FILLHATCH"; + return u"FILLHATCH"_ustr; case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D: - return "GRAPHIC"; + return u"GRAPHIC"_ustr; case PRIMITIVE2D_ID_GRIDPRIMITIVE2D: - return "GRID"; + return u"GRID"_ustr; case PRIMITIVE2D_ID_GROUPPRIMITIVE2D: - return "GROUP"; + return u"GROUP"_ustr; case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D: - return "HELPLINE"; + return u"HELPLINE"_ustr; case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: - return "MARKERARRAY"; + return u"MARKERARRAY"_ustr; case PRIMITIVE2D_ID_MASKPRIMITIVE2D: - return "MASK"; + return u"MASK"_ustr; case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D: - return "MEDIA"; + return u"MEDIA"_ustr; case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: - return "METAFILE"; + return u"METAFILE"_ustr; case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: - return "MODIFIEDCOLOR"; + return u"MODIFIEDCOLOR"_ustr; case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: - return "POLYGONHAIRLINE"; + return u"POLYGONHAIRLINE"_ustr; case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D: - return "POLYGONMARKER"; + return u"POLYGONMARKER"_ustr; case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: - return "POLYGONSTROKE"; + return u"POLYGONSTROKE"_ustr; case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D: - return "POLYGONSTROKEARROW"; + return u"POLYGONSTROKEARROW"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D: - return "POLYPOLYGONSTROKE"; + return u"POLYPOLYGONSTROKE"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONSTROKEARROWPRIMITIVE2D: - return "POLYPOLYGONSTROKEARROW"; + return u"POLYPOLYGONSTROKEARROW"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: - return "POLYPOLYGONCOLOR"; + return u"POLYPOLYGONCOLOR"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D: - return "POLYPOLYGONGRADIENT"; + return u"POLYPOLYGONGRADIENT"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D: - return "POLYPOLYGONHATCH"; + return u"POLYPOLYGONHATCH"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: - return "POLYPOLYGONGRAPHIC"; + return u"POLYPOLYGONGRAPHIC"_ustr; case PRIMITIVE2D_ID_SCENEPRIMITIVE2D: - return "SCENE"; + return u"SCENE"_ustr; case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D: - return "SHADOW"; + return u"SHADOW"_ustr; case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: - return "TEXTSIMPLEPORTION"; + return u"TEXTSIMPLEPORTION"_ustr; case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: - return "TEXTDECORATEDPORTION"; + return u"TEXTDECORATEDPORTION"_ustr; case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: - return "TRANSFORM"; + return u"TRANSFORM"_ustr; case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: - return "UNIFIEDTRANSPARENCE"; + return u"UNIFIEDTRANSPARENCE"_ustr; case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: - return "POINTARRAY"; + return u"POINTARRAY"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D: - return "TEXTHIERARCHYFIELD"; + return u"TEXTHIERARCHYFIELD"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D: - return "TEXTHIERARCHYLINE"; + return u"TEXTHIERARCHYLINE"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D: - return "TEXTHIERARCHYPARAGRAPH"; + return u"TEXTHIERARCHYPARAGRAPH"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D: - return "TEXTHIERARCHYBLOCK"; + return u"TEXTHIERARCHYBLOCK"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D: - return "TEXTHIERARCHYEDIT"; + return u"TEXTHIERARCHYEDIT"_ustr; case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D: - return "POLYGONWAVE"; + return u"POLYGONWAVE"_ustr; case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D: - return "WRONGSPELL"; + return u"WRONGSPELL"_ustr; case PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D: - return "TEXTEFFECT"; + return u"TEXTEFFECT"_ustr; case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D: - return "TEXTHIERARCHYBULLET"; + return u"TEXTHIERARCHYBULLET"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONHAIRLINEPRIMITIVE2D: - return "POLYPOLYGONHAIRLINE"; + return u"POLYPOLYGONHAIRLINE"_ustr; case PRIMITIVE2D_ID_EXECUTEPRIMITIVE2D: - return "EXECUTE"; + return u"EXECUTE"_ustr; case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D: - return "PAGEPREVIEW"; + return u"PAGEPREVIEW"_ustr; case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D: - return "STRUCTURETAG"; + return u"STRUCTURETAG"_ustr; case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: - return "BORDERLINE"; + return u"BORDERLINE"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONMARKERPRIMITIVE2D: - return "POLYPOLYGONMARKER"; + return u"POLYPOLYGONMARKER"_ustr; case PRIMITIVE2D_ID_HITTESTPRIMITIVE2D: - return "HITTEST"; + return u"HITTEST"_ustr; case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: - return "INVERT"; + return u"INVERT"_ustr; case PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D: - return "DISCRETEBITMAP"; + return u"DISCRETEBITMAP"_ustr; case PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D: - return "WALLPAPERBITMAP"; + return u"WALLPAPERBITMAP"_ustr; case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D: - return "TEXTLINE"; + return u"TEXTLINE"_ustr; case PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D: - return "TEXTCHARACTERSTRIKEOUT"; + return u"TEXTCHARACTERSTRIKEOUT"_ustr; case PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D: - return "TEXTGEOMETRYSTRIKEOUT"; + return u"TEXTGEOMETRYSTRIKEOUT"_ustr; case PRIMITIVE2D_ID_EPSPRIMITIVE2D: - return "EPS"; + return u"EPS"_ustr; case PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D: - return "DISCRETESHADOW"; + return u"DISCRETESHADOW"_ustr; case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D: - return "HIDDENGEOMETRY"; + return u"HIDDENGEOMETRY"_ustr; case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D: - return "SVGLINEARGRADIENT"; + return u"SVGLINEARGRADIENT"_ustr; case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D: - return "SVGRADIALGRADIENT"; + return u"SVGRADIALGRADIENT"_ustr; case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D: - return "SVGLINEARATOM"; + return u"SVGLINEARATOM"_ustr; case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D: - return "SVGRADIALATOM"; + return u"SVGRADIALATOM"_ustr; case PRIMITIVE2D_ID_CROPPRIMITIVE2D: - return "CROP"; + return u"CROP"_ustr; case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D: - return "PATTERNFILL"; + return u"PATTERNFILL"_ustr; case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D: - return "OBJECTINFO"; + return u"OBJECTINFO"_ustr; case PRIMITIVE2D_ID_POLYPOLYGONSELECTIONPRIMITIVE2D: - return "POLYPOLYGONSELECTION"; + return u"POLYPOLYGONSELECTION"_ustr; case PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D: - return "PAGEHIERARCHY"; + return u"PAGEHIERARCHY"_ustr; case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: - return "GLOWPRIMITIVE"; + return u"GLOWPRIMITIVE"_ustr; case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D: - return "SOFTEDGEPRIMITIVE"; + return u"SOFTEDGEPRIMITIVE"_ustr; + case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D: + return u"LINERECTANGLEPRIMITIVE"_ustr; + case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D: + return u"FILLEDRECTANGLEPRIMITIVE"_ustr; + case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D: + return u"SINGLELINEPRIMITIVE"_ustr; + case PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D: + return u"EXCLUSIVEEDITVIEWPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D: + return u"ANIMATEDGRAPHICPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_POLYPOLYGONRGBAPRIMITIVE2D: + return u"POLYPOLYGONRGBAPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D: + return u"BITMAPALPHAPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + return u"POLYPOLYGONALPHAGRADIENTPRIMITIVE2D"_ustr; + case PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D: + return u"TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D"_ustr; default: return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF); } diff --git a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx index d2c8d4a6571a..87f524180fbe 100644 --- a/drawinglayer/source/primitive2d/animatedprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/animatedprimitive2d.cxx @@ -37,9 +37,9 @@ namespace drawinglayer::primitive2d AnimatedSwitchPrimitive2D::AnimatedSwitchPrimitive2D( const animation::AnimationEntry& rAnimationEntry, - const Primitive2DContainer& rChildren, + Primitive2DContainer&& aChildren, bool bIsTextAnimation) - : GroupPrimitive2D(rChildren), + : GroupPrimitive2D(std::move(aChildren)), mbIsTextAnimation(bIsTextAnimation) { // clone given animation description @@ -76,12 +76,14 @@ namespace drawinglayer::primitive2d nIndex = nLen - 1; } - const Primitive2DReference xRef(getChildren()[nIndex], uno::UNO_SET_THROW); - rVisitor.append(xRef); + rVisitor.visit(getChildren()[nIndex]); } // provide unique ID - ImplPrimitive2DIDBlock(AnimatedSwitchPrimitive2D, PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D) + sal_uInt32 AnimatedSwitchPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D; + } } // end of namespace drawinglayer::primitive2d @@ -90,8 +92,8 @@ namespace drawinglayer::primitive2d { AnimatedBlinkPrimitive2D::AnimatedBlinkPrimitive2D( const animation::AnimationEntry& rAnimationEntry, - const Primitive2DContainer& rChildren) - : AnimatedSwitchPrimitive2D(rAnimationEntry, rChildren, true/*bIsTextAnimation*/) + Primitive2DContainer&& aChildren) + : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/) { } @@ -109,7 +111,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(AnimatedBlinkPrimitive2D, PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D) + sal_uInt32 AnimatedBlinkPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D; + } } // end of namespace drawinglayer::primitive2d @@ -119,17 +124,16 @@ namespace drawinglayer::primitive2d AnimatedInterpolatePrimitive2D::AnimatedInterpolatePrimitive2D( const std::vector< basegfx::B2DHomMatrix >& rmMatrixStack, const animation::AnimationEntry& rAnimationEntry, - const Primitive2DContainer& rChildren) - : AnimatedSwitchPrimitive2D(rAnimationEntry, rChildren, true/*bIsTextAnimation*/), - maMatrixStack() + Primitive2DContainer&& aChildren) + : AnimatedSwitchPrimitive2D(rAnimationEntry, std::move(aChildren), true/*bIsTextAnimation*/) { // copy matrices to locally pre-decomposed matrix stack const sal_uInt32 nCount(rmMatrixStack.size()); maMatrixStack.reserve(nCount); - for(sal_uInt32 a(0); a < nCount; a++) + for(const auto& a : rmMatrixStack) { - maMatrixStack.emplace_back(rmMatrixStack[a]); + maMatrixStack.emplace_back(a); } } @@ -179,8 +183,8 @@ namespace drawinglayer::primitive2d } // create new transform primitive reference, return new sequence - const Primitive2DReference xRef(new TransformPrimitive2D(aTargetTransform, getChildren())); - rVisitor.append(xRef); + Primitive2DReference xRef(new TransformPrimitive2D(aTargetTransform, Primitive2DContainer(getChildren()))); + rVisitor.visit(xRef); } else { @@ -189,8 +193,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(AnimatedInterpolatePrimitive2D, PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D) - + sal_uInt32 AnimatedInterpolatePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D; + } } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx index ba0f47193b49..7d73cdbee4d1 100644 --- a/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/backgroundcolorprimitive2d.cxx @@ -21,6 +21,7 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> @@ -30,22 +31,39 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void BackgroundColorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference BackgroundColorPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { - if(!rViewInformation.getViewport().isEmpty()) + // transparency invalid or completely transparent, done + if(getTransparency() < 0.0 || getTransparency() >= 1.0) + return nullptr; + + // no viewport, not visible, done + if(rViewInformation.getViewport().isEmpty()) + return nullptr; + + // create decompose geometry + const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport())); + + if (getTransparency() <= 0.0) { - const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rViewInformation.getViewport())); - rContainer.push_back(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor())); + // no transparency + return Primitive2DReference { + new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), getBColor()) }; } + + // if transparent, use PolyPolygonRGBAPrimitive2D + return Primitive2DReference { + new PolyPolygonRGBAPrimitive2D( + basegfx::B2DPolyPolygon(aOutline), + getBColor(), + getTransparency()) }; } BackgroundColorPrimitive2D::BackgroundColorPrimitive2D( const basegfx::BColor& rBColor, double fTransparency) - : BufferedDecompositionPrimitive2D(), - maBColor(rBColor), - mfTransparency(fTransparency), - maLastViewport() + : maBColor(rBColor), + mfTransparency(fTransparency) { } @@ -69,15 +87,13 @@ namespace drawinglayer::primitive2d void BackgroundColorPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - - if(!getBuffered2DDecomposition().empty() && (maLastViewport != rViewInformation.getViewport())) + if(hasBuffered2DDecomposition() && (maLastViewport != rViewInformation.getViewport())) { // conditions of last local decomposition have changed, delete - const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< BackgroundColorPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember ViewRange const_cast< BackgroundColorPrimitive2D* >(this)->maLastViewport = rViewInformation.getViewport(); @@ -88,7 +104,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(BackgroundColorPrimitive2D, PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D) + sal_uInt32 BackgroundColorPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/baseprimitive2d.cxx b/drawinglayer/source/primitive2d/baseprimitive2d.cxx index ef7055ebdaa4..a2e0eaf6b6ba 100644 --- a/drawinglayer/source/primitive2d/baseprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/baseprimitive2d.cxx @@ -19,23 +19,17 @@ #include <sal/config.h> -#include <iterator> -#include <utility> - +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <drawinglayer/primitive2d/baseprimitive2d.hxx> #include <drawinglayer/primitive2d/Tools.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <basegfx/utils/canvastools.hxx> -#include <comphelper/sequence.hxx> using namespace css; namespace drawinglayer::primitive2d { -BasePrimitive2D::BasePrimitive2D() - : BasePrimitive2DImplBase(m_aMutex) -{ -} +BasePrimitive2D::BasePrimitive2D() {} BasePrimitive2D::~BasePrimitive2D() {} @@ -57,15 +51,15 @@ public: : mrViewInformation(rViewInformation) { } - virtual void append(const Primitive2DReference& r) override + virtual void visit(const Primitive2DReference& r) override { maRetval.expand(getB2DRangeFromPrimitive2DReference(r, mrViewInformation)); } - virtual void append(const Primitive2DContainer& r) override + virtual void visit(const Primitive2DContainer& r) override { maRetval.expand(r.getB2DRange(mrViewInformation)); } - virtual void append(Primitive2DContainer&& r) override + virtual void visit(Primitive2DContainer&& r) override { maRetval.expand(r.getB2DRange(mrViewInformation)); } @@ -86,56 +80,27 @@ void BasePrimitive2D::get2DDecomposition( { } -css::uno::Sequence<::css::uno::Reference<::css::graphic::XPrimitive2D>> SAL_CALL +Primitive2DContainer BasePrimitive2D::getDecomposition(const uno::Sequence<beans::PropertyValue>& rViewParameters) { - const geometry::ViewInformation2D aViewInformation(rViewParameters); + const auto aViewInformation = geometry::createViewInformation2D(rViewParameters); Primitive2DContainer aContainer; get2DDecomposition(aContainer, aViewInformation); - return comphelper::containerToSequence(aContainer); + return aContainer; } -css::geometry::RealRectangle2D SAL_CALL +css::geometry::RealRectangle2D BasePrimitive2D::getRange(const uno::Sequence<beans::PropertyValue>& rViewParameters) { - const geometry::ViewInformation2D aViewInformation(rViewParameters); + const auto aViewInformation = geometry::createViewInformation2D(rViewParameters); return basegfx::unotools::rectangle2DFromB2DRectangle(getB2DRange(aViewInformation)); } -sal_Int64 SAL_CALL BasePrimitive2D::estimateUsage() +sal_Int64 BasePrimitive2D::estimateUsage() { return 0; // for now ignore the objects themselves } -void BufferedDecompositionPrimitive2D::create2DDecomposition( - Primitive2DContainer& /*rContainer*/, - const geometry::ViewInformation2D& /*rViewInformation*/) const -{ -} - -BufferedDecompositionPrimitive2D::BufferedDecompositionPrimitive2D() - : BasePrimitive2D() - , maBuffered2DDecomposition() -{ -} - -void BufferedDecompositionPrimitive2D::get2DDecomposition( - Primitive2DDecompositionVisitor& rVisitor, - const geometry::ViewInformation2D& rViewInformation) const -{ - ::osl::MutexGuard aGuard(m_aMutex); - - if (getBuffered2DDecomposition().empty()) - { - Primitive2DContainer aNewSequence; - create2DDecomposition(aNewSequence, rViewInformation); - const_cast<BufferedDecompositionPrimitive2D*>(this)->setBuffered2DDecomposition( - aNewSequence); - } - - rVisitor.append(getBuffered2DDecomposition()); -} - } // end of namespace drawinglayer::primitive2d /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx index f495d531d8ff..82f45be77b6f 100644 --- a/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/bitmapprimitive2d.cxx @@ -20,16 +20,15 @@ #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <com/sun/star/awt/XBitmap.hpp> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { -BitmapPrimitive2D::BitmapPrimitive2D(const css::uno::Reference<css::awt::XBitmap>& rXBitmap, - const basegfx::B2DHomMatrix& rTransform) - : BasePrimitive2D() - , maXBitmap(rXBitmap) - , maTransform(rTransform) +BitmapPrimitive2D::BitmapPrimitive2D(Bitmap xXBitmap, basegfx::B2DHomMatrix aTransform) + : maBitmap(std::move(xXBitmap)) + , maTransform(std::move(aTransform)) { } @@ -39,7 +38,7 @@ bool BitmapPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { const BitmapPrimitive2D& rCompare = static_cast<const BitmapPrimitive2D&>(rPrimitive); - return (getXBitmap() == rCompare.getXBitmap() && getTransform() == rCompare.getTransform()); + return (getBitmap() == rCompare.getBitmap() && getTransform() == rCompare.getTransform()); } return false; @@ -53,25 +52,17 @@ BitmapPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInforma return aRetval; } -sal_Int64 SAL_CALL BitmapPrimitive2D::estimateUsage() +sal_Int64 BitmapPrimitive2D::estimateUsage() { - if (!getXBitmap().is()) + if (getBitmap().IsEmpty()) { return 0; } - - uno::Reference<util::XAccounting> const xAcc(getXBitmap(), uno::UNO_QUERY); - - if (!xAcc.is()) - { - return 0; - } - - return xAcc->estimateUsage(); + return getBitmap().GetSizeBytes(); } // provide unique ID -ImplPrimitive2DIDBlock(BitmapPrimitive2D, PRIMITIVE2D_ID_BITMAPPRIMITIVE2D) +sal_uInt32 BitmapPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_BITMAPPRIMITIVE2D; } } // end of namespace diff --git a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx index b264e2c028af..79e34ec972ce 100644 --- a/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/borderlineprimitive2d.cxx @@ -21,11 +21,13 @@ #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <basegfx/polygon/b2dpolygon.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <rtl/math.hxx> #include <algorithm> +#include <utility> namespace drawinglayer::primitive2d @@ -87,14 +89,14 @@ namespace drawinglayer::primitive2d { rContainer.push_back( new PolygonStrokePrimitive2D( - aPolygon, + std::move(aPolygon), rLineAttribute)); } else { rContainer.push_back( new PolygonStrokePrimitive2D( - aPolygon, + std::move(aPolygon), rLineAttribute, rStrokeAttribute)); } @@ -112,10 +114,10 @@ namespace drawinglayer::primitive2d return fRetval; } - void BorderLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference BorderLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { if (getStart().equal(getEnd()) || getBorderLines().empty()) - return; + return nullptr; // get data and vectors basegfx::B2DVector aVector(getEnd() - getStart()); @@ -124,6 +126,7 @@ namespace drawinglayer::primitive2d const double fFullWidth(getFullWidth()); double fOffset(fFullWidth * -0.5); + Primitive2DContainer aContainer; for(const auto& candidate : maBorderLines) { const double fWidth(candidate.getLineAttribute().getWidth()); @@ -141,7 +144,7 @@ namespace drawinglayer::primitive2d // start and end extends lead to an edge perpendicular to the line, so we can just use // a PolygonStrokePrimitive2D for representation addPolygonStrokePrimitive2D( - rContainer, + aContainer, aStart - (aVector * candidate.getStartLeft()), aEnd + (aVector * candidate.getEndLeft()), candidate.getLineAttribute(), @@ -162,7 +165,7 @@ namespace drawinglayer::primitive2d aPolygon.append(aEnd + aHalfLineOffset + (aVector * candidate.getEndRight())); aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight())); - rContainer.push_back( + aContainer.push_back( new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aPolygon), candidate.getLineAttribute().getColor())); @@ -197,7 +200,7 @@ namespace drawinglayer::primitive2d aPolygon.append(aStart + aHalfLineOffset - (aVector * candidate.getStartRight())); } - rContainer.push_back( + aContainer.push_back( new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aPolygon), candidate.getLineAttribute().getColor())); @@ -226,7 +229,7 @@ namespace drawinglayer::primitive2d aPolygon.append(aEnd + aHalfLineOffset + (aVector * fMin)); aPolygon.append(aEnd - aHalfLineOffset + (aVector * fMin)); - rContainer.push_back( + aContainer.push_back( new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aPolygon), candidate.getLineAttribute().getColor())); @@ -236,7 +239,7 @@ namespace drawinglayer::primitive2d } addPolygonStrokePrimitive2D( - rContainer, + aContainer, aStrokeStart, aStrokeEnd, candidate.getLineAttribute(), @@ -247,6 +250,7 @@ namespace drawinglayer::primitive2d fOffset += fWidth; } + return new GroupPrimitive2D(std::move(aContainer)); } bool BorderLinePrimitive2D::isHorizontalOrVertical(const geometry::ViewInformation2D& rViewInformation) const @@ -265,44 +269,33 @@ namespace drawinglayer::primitive2d BorderLinePrimitive2D::BorderLinePrimitive2D( const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd, - const std::vector< BorderLine >& rBorderLines, - const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute) - : BufferedDecompositionPrimitive2D(), - maStart(rStart), + std::vector< BorderLine >&& rBorderLines, + drawinglayer::attribute::StrokeAttribute aStrokeAttribute) + : maStart(rStart), maEnd(rEnd), - maBorderLines(rBorderLines), - maStrokeAttribute(rStrokeAttribute) + maBorderLines(std::move(rBorderLines)), + maStrokeAttribute(std::move(aStrokeAttribute)) { } bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive); + if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + return false; - if (getStart() == rCompare.getStart() - && getEnd() == rCompare.getEnd() - && getStrokeAttribute() == rCompare.getStrokeAttribute()) - { - if (getBorderLines().size() == rCompare.getBorderLines().size()) - { - for (size_t a(0); a < getBorderLines().size(); a++) - { - if (!(getBorderLines()[a] == rCompare.getBorderLines()[a])) - { - return false; - } - } - } - } - } + const BorderLinePrimitive2D& rCompare = static_cast<const BorderLinePrimitive2D&>(rPrimitive); - return false; + return (getStart() == rCompare.getStart() + && getEnd() == rCompare.getEnd() + && getStrokeAttribute() == rCompare.getStrokeAttribute() + && getBorderLines() == rCompare.getBorderLines()); } // provide unique ID - ImplPrimitive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D) + sal_uInt32 BorderLinePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D; + } Primitive2DReference tryMergeBorderLinePrimitive2D( const BorderLinePrimitive2D* pCandidateA, @@ -338,7 +331,7 @@ namespace drawinglayer::primitive2d // direction has to be equal -> cross product == 0.0 const basegfx::B2DVector aVT(pCandidateA->getEnd() - pCandidateA->getStart()); const basegfx::B2DVector aVC(pCandidateB->getEnd() - pCandidateB->getStart()); - if(!rtl::math::approxEqual(0.0, aVC.cross(aVT))) + if(aVC.cross(aVT) != 0) { return Primitive2DReference(); } @@ -415,12 +408,12 @@ namespace drawinglayer::primitive2d } } - return Primitive2DReference( + return new BorderLinePrimitive2D( pCandidateA->getStart(), pCandidateB->getEnd(), - aMergedBorderLines, - pCandidateA->getStrokeAttribute())); + std::move(aMergedBorderLines), + pCandidateA->getStrokeAttribute()); } } // end of namespace diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx index c3b030dcf1ac..de20ede62c24 100644 --- a/drawinglayer/source/primitive2d/controlprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx @@ -18,26 +18,30 @@ */ #include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <comphelper/processfactory.hxx> #include <com/sun/star/awt/XControl.hpp> #include <com/sun/star/uno/XComponentContext.hpp> #include <drawinglayer/geometry/viewinformation2d.hxx> +#include <utility> +#include <rtl/ustrbuf.hxx> #include <vcl/virdev.hxx> #include <vcl/svapp.hxx> #include <com/sun/star/awt/PosSize.hpp> -#include <vcl/bitmapex.hxx> +#include <com/sun/star/awt/XWindow2.hpp> +#include <vcl/bitmap.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> -#include <tools/diagnose_ex.h> +#include <comphelper/diagnose_ex.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolygon.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <svtools/optionsdrawinglayer.hxx> -#include <toolkit/awt/vclxwindow.hxx> #include <vcl/window.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <toolkit/helper/vclunohelper.hxx> +#include <officecfg/Office/Common.hxx> using namespace com::sun::star; @@ -45,7 +49,7 @@ namespace drawinglayer::primitive2d { void ControlPrimitive2D::createXControl() { - if(!(!mxXControl.is() && getControlModel().is())) + if(mxXControl.is() || !getControlModel().is()) return; uno::Reference< beans::XPropertySet > xSet(getControlModel(), uno::UNO_QUERY); @@ -53,7 +57,7 @@ namespace drawinglayer::primitive2d if(!xSet.is()) return; - uno::Any aValue(xSet->getPropertyValue("DefaultControl")); + uno::Any aValue(xSet->getPropertyValue(u"DefaultControl"_ustr)); OUString aUnoControlTypeName; if(!(aValue >>= aUnoControlTypeName)) @@ -62,7 +66,7 @@ namespace drawinglayer::primitive2d if(aUnoControlTypeName.isEmpty()) return; - uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() ); uno::Reference< awt::XControl > xXControl( xContext->getServiceManager()->createInstanceWithContext(aUnoControlTypeName, xContext), uno::UNO_QUERY); @@ -71,7 +75,7 @@ namespace drawinglayer::primitive2d xXControl->setModel(getControlModel()); // remember XControl - mxXControl = xXControl; + mxXControl = std::move(xXControl); } } @@ -96,8 +100,7 @@ namespace drawinglayer::primitive2d basegfx::B2DVector aDiscreteSize(rViewInformation.getObjectToViewTransformation() * aScale); // limit to a maximum square size, e.g. 300x150 pixels (45000) - const SvtOptionsDrawinglayer aDrawinglayerOpt; - const double fDiscreteMax(aDrawinglayerOpt.GetQuadraticFormControlRenderLimit()); + const double fDiscreteMax(officecfg::Office::Common::Drawinglayer::QuadraticFormControlRenderLimit::get()); const double fDiscreteQuadratic(aDiscreteSize.getX() * aDiscreteSize.getY()); const bool bScaleUsed(fDiscreteQuadratic > fDiscreteMax); double fFactor(1.0); @@ -144,26 +147,12 @@ namespace drawinglayer::primitive2d if(xControl.is()) { - uno::Reference< awt::XWindowPeer > xWindowPeer(xControl->getPeer()); - - if(xWindowPeer.is()) + uno::Reference<awt::XWindowPeer> xWindowPeer(xControl->getPeer()); + if (xWindowPeer) { - VCLXWindow* pVCLXWindow = comphelper::getUnoTunnelImplementation<VCLXWindow>(xWindowPeer); - - if(pVCLXWindow) - { - VclPtr<vcl::Window> pWindow = pVCLXWindow->GetWindow(); - - if(pWindow) - { - pWindow = pWindow->GetParent(); - - if(pWindow && MapUnit::Map100thMM == pWindow->GetMapMode().GetMapUnit()) - { - bUserIs100thmm = true; - } - } - } + uno::Reference<awt::XVclWindowPeer> xPeerProps(xWindowPeer, uno::UNO_QUERY_THROW); + uno::Any aAny = xPeerProps->getProperty(u"ParentIs100thmm"_ustr); // see VCLXWindow::getProperty + aAny >>= bUserIs100thmm; } } @@ -187,27 +176,21 @@ namespace drawinglayer::primitive2d xControlView->draw(0, 0); // get bitmap - const BitmapEx aContent(aVirtualDevice->GetBitmapEx(Point(), aSizePixel)); + const Bitmap aContent(aVirtualDevice->GetBitmap(Point(), aSizePixel)); - // to avoid scaling, use the Bitmap pixel size as primitive size - const Size aBitmapSize(aContent.GetSizePixel()); - basegfx::B2DVector aBitmapSizeLogic( - rViewInformation.getInverseObjectToViewTransformation() * - basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1)); - - if(bScaleUsed) - { - // if scaled adapt to scaled size - aBitmapSizeLogic /= fFactor; - } + // snap translate and scale to discrete position (pixel) to avoid sub-pixel offset and blurring further + basegfx::B2DVector aSnappedTranslate(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aTranslate)); + aSnappedTranslate = rViewInformation.getInverseObjectToViewTransformation() * aSnappedTranslate; + basegfx::B2DVector aSnappedScale(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aScale)); + aSnappedScale = rViewInformation.getInverseObjectToViewTransformation() * aSnappedScale; // short form for scale and translate transformation const basegfx::B2DHomMatrix aBitmapTransform(basegfx::utils::createScaleTranslateB2DHomMatrix( - aBitmapSizeLogic.getX(), aBitmapSizeLogic.getY(), aTranslate.getX(), aTranslate.getY())); + aSnappedScale.getX(), aSnappedScale.getY(), aSnappedTranslate.getX(), aSnappedTranslate.getY())); // create primitive xRetval = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(aContent), + aContent, aBitmapTransform); } catch( const uno::Exception& ) @@ -227,16 +210,16 @@ namespace drawinglayer::primitive2d // create a gray placeholder hairline polygon in object size basegfx::B2DRange aObjectRange(0.0, 0.0, 1.0, 1.0); aObjectRange.transform(getTransform()); - const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange)); + basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aObjectRange)); const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0); // The replacement object may also get a text like 'empty group' here later - Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(aOutline, aGrayTone)); + Primitive2DReference xRetval(new PolygonHairlinePrimitive2D(std::move(aOutline), aGrayTone)); return xRetval; } - void ControlPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference ControlPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { // try to create a bitmap decomposition. If that fails for some reason, // at least create a replacement decomposition. @@ -247,30 +230,28 @@ namespace drawinglayer::primitive2d xReference = createPlaceholderDecomposition(); } - rContainer.push_back(xReference); + return xReference; } ControlPrimitive2D::ControlPrimitive2D( - const basegfx::B2DHomMatrix& rTransform, - const uno::Reference< awt::XControlModel >& rxControlModel) - : BufferedDecompositionPrimitive2D(), - maTransform(rTransform), - mxControlModel(rxControlModel), - mxXControl(), - maLastViewScaling() - { - } - - ControlPrimitive2D::ControlPrimitive2D( - const basegfx::B2DHomMatrix& rTransform, - const uno::Reference< awt::XControlModel >& rxControlModel, - const uno::Reference< awt::XControl >& rxXControl) - : BufferedDecompositionPrimitive2D(), - maTransform(rTransform), - mxControlModel(rxControlModel), - mxXControl(rxXControl), - maLastViewScaling() + basegfx::B2DHomMatrix aTransform, + uno::Reference< awt::XControlModel > xControlModel, + uno::Reference<awt::XControl> xXControl, + ::std::u16string_view const rTitle, + ::std::u16string_view const rDescription, + void const*const pAnchorKey) + : maTransform(std::move(aTransform)), + mxControlModel(std::move(xControlModel)), + mxXControl(std::move(xXControl)) + , m_pAnchorStructureElementKey(pAnchorKey) { + ::rtl::OUStringBuffer buf(rTitle); + if (!rTitle.empty() && !rDescription.empty()) + { + buf.append(" - "); + } + buf.append(rDescription); + m_AltText = buf.makeStringAndClear(); } const uno::Reference< awt::XControl >& ControlPrimitive2D::getXControl() const @@ -286,38 +267,37 @@ namespace drawinglayer::primitive2d bool ControlPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { // use base class compare operator - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive); + if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + return false; - if(getTransform() == rCompare.getTransform()) - { - // check if ControlModel references both are/are not - bool bRetval(getControlModel().is() == rCompare.getControlModel().is()); + const ControlPrimitive2D& rCompare = static_cast<const ControlPrimitive2D&>(rPrimitive); - if(bRetval && getControlModel().is()) - { - // both exist, check for equality - bRetval = (getControlModel() == rCompare.getControlModel()); - } + if(getTransform() != rCompare.getTransform()) + return false; - if(bRetval) - { - // check if XControl references both are/are not - bRetval = (getXControl().is() == rCompare.getXControl().is()); - } + // check if ControlModel references both are/are not + if (getControlModel().is() != rCompare.getControlModel().is()) + return false; - if(bRetval && getXControl().is()) - { - // both exist, check for equality - bRetval = (getXControl() == rCompare.getXControl()); - } + if(getControlModel().is()) + { + // both exist, check for equality + if (getControlModel() != rCompare.getControlModel()) + return false; + } - return bRetval; - } + // check if XControl references both are/are not + if (getXControl().is() != rCompare.getXControl().is()) + return false; + + if(getXControl().is()) + { + // both exist, check for equality + if (getXControl() != rCompare.getXControl()) + return false; } - return false; + return true; } basegfx::B2DRange ControlPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const @@ -328,23 +308,41 @@ namespace drawinglayer::primitive2d return aRetval; } + bool ControlPrimitive2D::isVisibleAsChildWindow() const + { + // find out if the control is already visualized as a VCL-ChildWindow + const uno::Reference<awt::XControl>& rXControl(getXControl()); + + try + { + uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW); + return rXControl->getPeer().is() && xControlWindow->isVisible(); + } + catch (const uno::Exception&) + { + // #i116763# since there is a good alternative when the xControlView + // is not found and it is allowed to happen + } + + return false; + } + void ControlPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { // this primitive is view-dependent related to the scaling. If scaling has changed, // destroy existing decomposition. To detect change, use size of unit size in view coordinates - ::osl::MutexGuard aGuard( m_aMutex ); const basegfx::B2DVector aNewScaling(rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - if(!getBuffered2DDecomposition().empty()) + if(hasBuffered2DDecomposition()) { if(!maLastViewScaling.equal(aNewScaling)) { // conditions of last local decomposition have changed, delete - const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ControlPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember ViewTransformation const_cast< ControlPrimitive2D* >(this)->maLastViewScaling = aNewScaling; @@ -355,7 +353,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(ControlPrimitive2D, PRIMITIVE2D_ID_CONTROLPRIMITIVE2D) + sal_uInt32 ControlPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_CONTROLPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/cropprimitive2d.cxx b/drawinglayer/source/primitive2d/cropprimitive2d.cxx index 2d780ac2645c..da28d9e41663 100644 --- a/drawinglayer/source/primitive2d/cropprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/cropprimitive2d.cxx @@ -24,6 +24,7 @@ #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <utility> using namespace com::sun::star; @@ -32,14 +33,14 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { CropPrimitive2D::CropPrimitive2D( - const Primitive2DContainer& rChildren, - const basegfx::B2DHomMatrix& rTransformation, + Primitive2DContainer&& aChildren, + basegfx::B2DHomMatrix aTransformation, double fCropLeft, double fCropTop, double fCropRight, double fCropBottom) - : GroupPrimitive2D(rChildren), - maTransformation(rTransformation), + : GroupPrimitive2D(std::move(aChildren)), + maTransformation(std::move(aTransformation)), mfCropLeft(fCropLeft), mfCropTop(fCropTop), mfCropRight(fCropRight), @@ -120,13 +121,13 @@ namespace drawinglayer::primitive2d const Primitive2DReference xTransformPrimitive( new TransformPrimitive2D( aNewTransform, - getChildren())); + Primitive2DContainer(getChildren()))); if(aUnitRange.isInside(aNewRange)) { // the new range is completely inside the old range (unit range), // so no masking is needed - rVisitor.append(xTransformPrimitive); + rVisitor.visit(xTransformPrimitive); } else { @@ -137,15 +138,18 @@ namespace drawinglayer::primitive2d // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector const Primitive2DReference xMask( new MaskPrimitive2D( - aMaskPolyPolygon, + std::move(aMaskPolyPolygon), Primitive2DContainer { xTransformPrimitive })); - rVisitor.append(xMask); + rVisitor.visit(xMask); } } // provide unique ID - ImplPrimitive2DIDBlock(CropPrimitive2D, PRIMITIVE2D_ID_CROPPRIMITIVE2D) + sal_uInt32 CropPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_CROPPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx index 17d6f2301a43..610df57a95e3 100644 --- a/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/discretebitmapprimitive2d.cxx @@ -25,18 +25,18 @@ namespace drawinglayer::primitive2d { - void DiscreteBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference DiscreteBitmapPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { // use getViewTransformation() and getObjectTransformation() from // ObjectAndViewTransformationDependentPrimitive2D to create a BitmapPrimitive2D // with the correct mapping - if(getBitmapEx().IsEmpty()) - return; + if(getBitmap().IsEmpty()) + return nullptr; // get discrete size - const Size& rSizePixel = getBitmapEx().GetSizePixel(); - const basegfx::B2DVector aDiscreteSize(rSizePixel.Width(), rSizePixel.Height()); + const Size aSizePixel = getBitmap().GetSizePixel(); + const basegfx::B2DVector aDiscreteSize(aSizePixel.Width(), aSizePixel.Height()); // get inverse ViewTransformation basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation()); @@ -64,17 +64,16 @@ namespace drawinglayer::primitive2d aObjectTransform = aInverseObjectTransformation * aObjectTransform; // create BitmapPrimitive2D with now object-local coordinate data - rContainer.push_back( + return new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()), - aObjectTransform)); + getBitmap(), + aObjectTransform); } DiscreteBitmapPrimitive2D::DiscreteBitmapPrimitive2D( - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, const basegfx::B2DPoint& rTopLeft) - : ObjectAndViewTransformationDependentPrimitive2D(), - maBitmapEx(rBitmapEx), + : maBitmap(rBitmap), maTopLeft(rTopLeft) { } @@ -85,7 +84,7 @@ namespace drawinglayer::primitive2d { const DiscreteBitmapPrimitive2D& rCompare = static_cast<const DiscreteBitmapPrimitive2D&>(rPrimitive); - return (getBitmapEx() == rCompare.getBitmapEx() + return (getBitmap() == rCompare.getBitmap() && getTopLeft() == rCompare.getTopLeft()); } @@ -93,7 +92,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(DiscreteBitmapPrimitive2D, PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D) + sal_uInt32 DiscreteBitmapPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_DISCRETEBITMAPPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx index 3100db147345..0e48bf1ff78b 100644 --- a/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/discreteshadowprimitive2d.cxx @@ -23,37 +23,31 @@ #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> +#include <osl/diagnose.h> #include <toolkit/helper/vclunohelper.hxx> namespace drawinglayer::primitive2d { - DiscreteShadow::DiscreteShadow(const BitmapEx& rBitmapEx) - : maBitmapEx(rBitmapEx), - maTopLeft(), - maTop(), - maTopRight(), - maRight(), - maBottomRight(), - maBottom(), - maBottomLeft(), - maLeft() + DiscreteShadow::DiscreteShadow(const Bitmap& rBitmap) + : maBitmap(rBitmap) { - const Size& rBitmapSize = getBitmapEx().GetSizePixel(); + maBitmap.Invert(); // convert transparency to alpha + const Size aBitmapSize = getBitmap().GetSizePixel(); - if(rBitmapSize.Width() != rBitmapSize.Height() || rBitmapSize.Width() < 7) + if(aBitmapSize.Width() != aBitmapSize.Height() || aBitmapSize.Width() < 7) { OSL_ENSURE(false, "DiscreteShadowPrimitive2D: wrong bitmap format (!)"); - maBitmapEx = BitmapEx(); + maBitmap = Bitmap(); } } - const BitmapEx& DiscreteShadow::getTopLeft() const + const Bitmap& DiscreteShadow::getTopLeft() const { if(maTopLeft.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maTopLeft = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maTopLeft = getBitmap(); const_cast< DiscreteShadow* >(this)->maTopLeft.Crop( ::tools::Rectangle(Point(0, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1))); } @@ -61,12 +55,12 @@ namespace drawinglayer::primitive2d return maTopLeft; } - const BitmapEx& DiscreteShadow::getTop() const + const Bitmap& DiscreteShadow::getTop() const { if(maTop.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maTop = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maTop = getBitmap(); const_cast< DiscreteShadow* >(this)->maTop.Crop( ::tools::Rectangle(Point((nQuarter * 2) + 1, 0), Size(1, nQuarter))); } @@ -74,12 +68,12 @@ namespace drawinglayer::primitive2d return maTop; } - const BitmapEx& DiscreteShadow::getTopRight() const + const Bitmap& DiscreteShadow::getTopRight() const { if(maTopRight.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maTopRight = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maTopRight = getBitmap(); const_cast< DiscreteShadow* >(this)->maTopRight.Crop( ::tools::Rectangle(Point((nQuarter * 2) + 2, 0), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1))); } @@ -87,12 +81,12 @@ namespace drawinglayer::primitive2d return maTopRight; } - const BitmapEx& DiscreteShadow::getRight() const + const Bitmap& DiscreteShadow::getRight() const { if(maRight.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maRight = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maRight = getBitmap(); const_cast< DiscreteShadow* >(this)->maRight.Crop( ::tools::Rectangle(Point((nQuarter * 3) + 3, (nQuarter * 2) + 1), Size(nQuarter, 1))); } @@ -100,12 +94,12 @@ namespace drawinglayer::primitive2d return maRight; } - const BitmapEx& DiscreteShadow::getBottomRight() const + const Bitmap& DiscreteShadow::getBottomRight() const { if(maBottomRight.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maBottomRight = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maBottomRight = getBitmap(); const_cast< DiscreteShadow* >(this)->maBottomRight.Crop( ::tools::Rectangle(Point((nQuarter * 2) + 2, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1))); } @@ -113,12 +107,12 @@ namespace drawinglayer::primitive2d return maBottomRight; } - const BitmapEx& DiscreteShadow::getBottom() const + const Bitmap& DiscreteShadow::getBottom() const { if(maBottom.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maBottom = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maBottom = getBitmap(); const_cast< DiscreteShadow* >(this)->maBottom.Crop( ::tools::Rectangle(Point((nQuarter * 2) + 1, (nQuarter * 3) + 3), Size(1, nQuarter))); } @@ -126,12 +120,12 @@ namespace drawinglayer::primitive2d return maBottom; } - const BitmapEx& DiscreteShadow::getBottomLeft() const + const Bitmap& DiscreteShadow::getBottomLeft() const { if(maBottomLeft.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maBottomLeft = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maBottomLeft = getBitmap(); const_cast< DiscreteShadow* >(this)->maBottomLeft.Crop( ::tools::Rectangle(Point(0, (nQuarter * 2) + 2), Size((nQuarter * 2) + 1, (nQuarter * 2) + 1))); } @@ -139,12 +133,12 @@ namespace drawinglayer::primitive2d return maBottomLeft; } - const BitmapEx& DiscreteShadow::getLeft() const + const Bitmap& DiscreteShadow::getLeft() const { if(maLeft.IsEmpty()) { - const sal_Int32 nQuarter((getBitmapEx().GetSizePixel().Width() - 3) >> 2); - const_cast< DiscreteShadow* >(this)->maLeft = getBitmapEx(); + const sal_Int32 nQuarter((getBitmap().GetSizePixel().Width() - 3) >> 2); + const_cast< DiscreteShadow* >(this)->maLeft = getBitmap(); const_cast< DiscreteShadow* >(this)->maLeft.Crop( ::tools::Rectangle(Point(0, (nQuarter * 2) + 1), Size(nQuarter, 1))); } @@ -157,14 +151,14 @@ namespace drawinglayer::primitive2d namespace drawinglayer::primitive2d { - void DiscreteShadowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference DiscreteShadowPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { Primitive2DContainer xRetval; - if(getDiscreteShadow().getBitmapEx().IsEmpty()) - return; + if(getDiscreteShadow().getBitmap().IsEmpty()) + return nullptr; - const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2); + const sal_Int32 nQuarter((getDiscreteShadow().getBitmap().GetSizePixel().Width() - 3) >> 2); const basegfx::B2DVector aScale(getTransform() * basegfx::B2DVector(1.0, 1.0)); const double fSingleX(getDiscreteUnit() / aScale.getX()); const double fSingleY(getDiscreteUnit() / aScale.getY()); @@ -176,97 +170,96 @@ namespace drawinglayer::primitive2d xRetval.resize(8); // TopLeft - xRetval[0] = Primitive2DReference( + xRetval[0] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopLeft()), + getDiscreteShadow().getTopLeft(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBigLenX, fBigLenY, -fBorderX, - -fBorderY))); + -fBorderY)); // Top - xRetval[1] = Primitive2DReference( + xRetval[1] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTop()), + getDiscreteShadow().getTop(), basegfx::utils::createScaleTranslateB2DHomMatrix( 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX, fBorderY, fBorderX + fSingleX, - -fBorderY))); + -fBorderY)); // TopRight - xRetval[2] = Primitive2DReference( + xRetval[2] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getTopRight()), + getDiscreteShadow().getTopRight(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBigLenX, fBigLenY, 1.0 - fBorderX, - -fBorderY))); + -fBorderY)); // Right - xRetval[3] = Primitive2DReference( + xRetval[3] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getRight()), + getDiscreteShadow().getRight(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBorderX, 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY, 1.0 + fSingleX, - fBorderY + fSingleY))); + fBorderY + fSingleY)); // BottomRight - xRetval[4] = Primitive2DReference( + xRetval[4] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomRight()), + getDiscreteShadow().getBottomRight(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBigLenX, fBigLenY, 1.0 - (fBorderX + fSingleX) + fSingleX, - 1.0 - (fBorderY + fSingleY) + fSingleY))); + 1.0 - (fBorderY + fSingleY) + fSingleY)); // Bottom - xRetval[5] = Primitive2DReference( + xRetval[5] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottom()), + getDiscreteShadow().getBottom(), basegfx::utils::createScaleTranslateB2DHomMatrix( 1.0 - (2.0 * (fBorderX + fSingleX)) + fSingleX, fBorderY, fBorderX + fSingleX, - 1.0 + fSingleY))); + 1.0 + fSingleY)); // BottomLeft - xRetval[6] = Primitive2DReference( + xRetval[6] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getBottomLeft()), + getDiscreteShadow().getBottomLeft(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBigLenX, fBigLenY, -fBorderX, - 1.0 - fBorderY))); + 1.0 - fBorderY)); // Left - xRetval[7] = Primitive2DReference( + xRetval[7] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getDiscreteShadow().getLeft()), + getDiscreteShadow().getLeft(), basegfx::utils::createScaleTranslateB2DHomMatrix( fBorderX, 1.0 - (2.0 * (fBorderY + fSingleY)) + fSingleY, -fBorderX, - fBorderY + fSingleY))); + fBorderY + fSingleY)); // put all in object transformation to get to target positions - rContainer.push_back( + return new TransformPrimitive2D( getTransform(), - xRetval)); + std::move(xRetval)); } DiscreteShadowPrimitive2D::DiscreteShadowPrimitive2D( const basegfx::B2DHomMatrix& rTransform, const DiscreteShadow& rDiscreteShadow) - : DiscreteMetricDependentPrimitive2D(), - maTransform(rTransform), + : maTransform(rTransform), maDiscreteShadow(rDiscreteShadow) { } @@ -286,7 +279,7 @@ namespace drawinglayer::primitive2d basegfx::B2DRange DiscreteShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const { - if(getDiscreteShadow().getBitmapEx().IsEmpty()) + if(getDiscreteShadow().getBitmap().IsEmpty()) { // no graphics without valid bitmap definition return basegfx::B2DRange(); @@ -299,7 +292,7 @@ namespace drawinglayer::primitive2d // extract discrete shadow size and grow const basegfx::B2DVector aScale(rViewInformation.getViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - const sal_Int32 nQuarter((getDiscreteShadow().getBitmapEx().GetSizePixel().Width() - 3) >> 2); + const sal_Int32 nQuarter((getDiscreteShadow().getBitmap().GetSizePixel().Width() - 3) >> 2); const double fGrowX((1.0 / aScale.getX()) * nQuarter); const double fGrowY((1.0 / aScale.getY()) * nQuarter); aRetval.grow(std::max(fGrowX, fGrowY)); @@ -309,7 +302,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(DiscreteShadowPrimitive2D, PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D) + sal_uInt32 DiscreteShadowPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_DISCRETESHADOWPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx index e38afeb4d612..6c7ebd113a43 100644 --- a/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/embedded3dprimitive2d.cxx @@ -21,11 +21,12 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/color/bcolor.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/geometry/viewinformation3d.hxx> #include <processor3d/shadow3dextractor.hxx> +#include <utility> using namespace com::sun::star; @@ -35,8 +36,6 @@ namespace drawinglayer::primitive2d { bool Embedded3DPrimitive2D::impGetShadow3D() const { - osl::MutexGuard aGuard( m_aMutex ); - // create on demand if(!mbShadow3DChecked && !getChildren3D().empty()) { @@ -60,31 +59,28 @@ namespace drawinglayer::primitive2d return !maShadowPrimitives.empty(); } - void Embedded3DPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference Embedded3DPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { // use info to create a yellow 2d rectangle, similar to empty 3d scenes and/or groups const basegfx::B2DRange aLocal2DRange(getB2DRange(rViewInformation)); - const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aLocal2DRange)); + basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aLocal2DRange)); const basegfx::BColor aYellow(1.0, 1.0, 0.0); - rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, aYellow)); + return new PolygonHairlinePrimitive2D(std::move(aOutline), aYellow); } Embedded3DPrimitive2D::Embedded3DPrimitive2D( - const primitive3d::Primitive3DContainer& rxChildren3D, - const basegfx::B2DHomMatrix& rObjectTransformation, - const geometry::ViewInformation3D& rViewInformation3D, + primitive3d::Primitive3DContainer aChildren3D, + basegfx::B2DHomMatrix aObjectTransformation, + geometry::ViewInformation3D aViewInformation3D, const basegfx::B3DVector& rLightNormal, double fShadowSlant, const basegfx::B3DRange& rScene3DRange) - : BufferedDecompositionPrimitive2D(), - mxChildren3D(rxChildren3D), - maObjectTransformation(rObjectTransformation), - maViewInformation3D(rViewInformation3D), + : mxChildren3D(std::move(aChildren3D)), + maObjectTransformation(std::move(aObjectTransformation)), + maViewInformation3D(std::move(aViewInformation3D)), maLightNormal(rLightNormal), mfShadowSlant(fShadowSlant), maScene3DRange(rScene3DRange), - maShadowPrimitives(), - maB2DRange(), mbShadow3DChecked(false) { maLightNormal.normalize(); @@ -141,7 +137,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(Embedded3DPrimitive2D, PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D) + sal_uInt32 Embedded3DPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_EMBEDDED3DPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/epsprimitive2d.cxx b/drawinglayer/source/primitive2d/epsprimitive2d.cxx index b519547c00d7..760d5d764c41 100644 --- a/drawinglayer/source/primitive2d/epsprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/epsprimitive2d.cxx @@ -20,10 +20,11 @@ #include <drawinglayer/primitive2d/epsprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <utility> namespace drawinglayer::primitive2d { - void EpsPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference EpsPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { const GDIMetaFile& rSubstituteContent = getMetaFile(); @@ -33,20 +34,20 @@ namespace drawinglayer::primitive2d // To really use the Eps data, a renderer has to know and interpret this primitive // directly. - rContainer.push_back( + return new MetafilePrimitive2D( getEpsTransform(), - rSubstituteContent)); + rSubstituteContent); } + return nullptr; } EpsPrimitive2D::EpsPrimitive2D( - const basegfx::B2DHomMatrix& rEpsTransform, - const GfxLink& rGfxLink, + basegfx::B2DHomMatrix aEpsTransform, + GfxLink aGfxLink, const GDIMetaFile& rMetaFile) - : BufferedDecompositionPrimitive2D(), - maEpsTransform(rEpsTransform), - maGfxLink(rGfxLink), + : maEpsTransform(std::move(aEpsTransform)), + maGfxLink(std::move(aGfxLink)), maMetaFile(rMetaFile) { } @@ -75,7 +76,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(EpsPrimitive2D, PRIMITIVE2D_ID_EPSPRIMITIVE2D) + sal_uInt32 EpsPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_EPSPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx b/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx new file mode 100644 index 000000000000..a510b97b5b02 --- /dev/null +++ b/drawinglayer/source/primitive2d/exclusiveeditviewprimitive2d.cxx @@ -0,0 +1,55 @@ +/* -*- 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 <drawinglayer/primitive2d/exclusiveeditviewprimitive2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> + +using namespace com::sun::star; + +namespace drawinglayer::primitive2d +{ +ExclusiveEditViewPrimitive2D::ExclusiveEditViewPrimitive2D(Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) +{ +} + +basegfx::B2DRange +ExclusiveEditViewPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + return getChildren().getB2DRange(rViewInformation); +} + +void ExclusiveEditViewPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + // check if EditView is visualized. if yes, use content by calling parent class. if no, suppress it + if (rViewInformation.getEditViewActive()) + GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + +// provide unique ID +sal_uInt32 ExclusiveEditViewPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_EXCLUSIVEEDITVIEWPRIMITIVE2D; +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx index fba740e833c5..8c2e339f8fbb 100644 --- a/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillgradientprimitive2d.cxx @@ -23,6 +23,11 @@ #include <texture/texture.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <utility> +#include <algorithm> using namespace com::sun::star; @@ -30,293 +35,330 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void FillGradientPrimitive2D::generateMatricesAndColors( - std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) const + // Get the OuterColor. Take into account that for css::awt::GradientStyle_AXIAL + // this is the last one due to inverted gradient usage (see constructor there) + basegfx::BColor FillGradientPrimitive2D::getOuterColor() const { - rEntries.clear(); + if (getFillGradient().getColorStops().empty()) + return basegfx::BColor(); - // make sure steps is not too high/low - const basegfx::BColor aStart(getFillGradient().getStartColor()); - const basegfx::BColor aEnd(getFillGradient().getEndColor()); - const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); - sal_uInt32 nSteps(getFillGradient().getSteps()); + if (css::awt::GradientStyle_AXIAL == getFillGradient().getStyle()) + return getFillGradient().getColorStops().back().getStopColor(); - if(nSteps == 0) - { - nSteps = nMaxSteps; - } - - if(nSteps < 2) - { - nSteps = 2; - } + return getFillGradient().getColorStops().front().getStopColor(); + } - if(nSteps > nMaxSteps) + // Get the needed UnitPolygon dependent on the GradientStyle + basegfx::B2DPolygon FillGradientPrimitive2D::getUnitPolygon() const + { + if (css::awt::GradientStyle_RADIAL == getFillGradient().getStyle() + || css::awt::GradientStyle_ELLIPTICAL == getFillGradient().getStyle()) { - nSteps = nMaxSteps; + return basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0); } - nSteps = std::max(sal_uInt32(1), nSteps); + return basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0)); + } + void FillGradientPrimitive2D::generateMatricesAndColors( + std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback) const + { switch(getFillGradient().getStyle()) { - case attribute::GradientStyle::Linear: + default: // GradientStyle_MAKE_FIXED_SIZE + case css::awt::GradientStyle_LINEAR: { texture::GeoTexSvxGradientLinear aGradient( getDefinitionRange(), getOutputRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getAngle()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } - case attribute::GradientStyle::Axial: + case css::awt::GradientStyle_AXIAL: { texture::GeoTexSvxGradientAxial aGradient( getDefinitionRange(), getOutputRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getAngle()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } - case attribute::GradientStyle::Radial: + case css::awt::GradientStyle_RADIAL: { texture::GeoTexSvxGradientRadial aGradient( getDefinitionRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } - case attribute::GradientStyle::Elliptical: + case css::awt::GradientStyle_ELLIPTICAL: { texture::GeoTexSvxGradientElliptical aGradient( getDefinitionRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } - case attribute::GradientStyle::Square: + case css::awt::GradientStyle_SQUARE: { texture::GeoTexSvxGradientSquare aGradient( getDefinitionRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } - case attribute::GradientStyle::Rect: + case css::awt::GradientStyle_RECT: { texture::GeoTexSvxGradientRect aGradient( getDefinitionRange(), - aStart, - aEnd, - nSteps, + getFillGradient().getSteps(), + getFillGradient().getColorStops(), getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); - aGradient.appendTransformationsAndColors(rEntries, rOuterColor); + aGradient.appendTransformationsAndColors(aCallback); break; } } } - void FillGradientPrimitive2D::createOverlappingFill( - Primitive2DContainer& rContainer, - const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, - const basegfx::BColor& rOuterColor, - const basegfx::B2DPolygon& rUnitPolygon) const + Primitive2DReference FillGradientPrimitive2D::createFill(bool bOverlapping) const { - // create solid fill with outmost color - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - basegfx::B2DPolyPolygon( - basegfx::utils::createPolygonFromRect(getOutputRange())), - rOuterColor)); - - // create solid fill steps - for(size_t a(0); a < rEntries.size(); a++) + Primitive2DContainer aContainer; + if (bOverlapping) { - // create part polygon - basegfx::B2DPolygon aNewPoly(rUnitPolygon); - - aNewPoly.transform(rEntries[a].maB2DHomMatrix); - - // create solid fill - rContainer.push_back( + // OverlappingFill: create solid fill with outmost color + aContainer.push_back( new PolyPolygonColorPrimitive2D( - basegfx::B2DPolyPolygon(aNewPoly), - rEntries[a].maBColor)); - } - } - - void FillGradientPrimitive2D::createNonOverlappingFill( - Primitive2DContainer& rContainer, - const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, - const basegfx::BColor& rOuterColor, - const basegfx::B2DPolygon& rUnitPolygon) const - { - // get outmost visible range from object - basegfx::B2DRange aOutmostRange(getOutputRange()); - basegfx::B2DPolyPolygon aCombinedPolyPoly; - - if(!rEntries.empty()) - { - // extend aOutmostRange with first polygon - basegfx::B2DPolygon aFirstPoly(rUnitPolygon); - - aFirstPoly.transform(rEntries[0].maB2DHomMatrix); - aCombinedPolyPoly.append(aFirstPoly); - aOutmostRange.expand(aFirstPoly.getB2DRange()); + basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect(getOutputRange())), + getOuterColor())); + + // create solid fill steps by providing callback as lambda + auto aCallback([&aContainer,this]( + const basegfx::B2DHomMatrix& rMatrix, + const basegfx::BColor& rColor) + { + // create part polygon + basegfx::B2DPolygon aNewPoly(getUnitPolygon()); + aNewPoly.transform(rMatrix); + + // create solid fill + aContainer.push_back( + new PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aNewPoly), + rColor)); + }); + + // call value generator to trigger callbacks + generateMatricesAndColors(aCallback); } - - // add outmost range to combined polypolygon (in 1st place), create first primitive - aCombinedPolyPoly.insert(0, basegfx::utils::createPolygonFromRect(aOutmostRange)); - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - aCombinedPolyPoly, - rOuterColor)); - - if(rEntries.empty()) - return; - - // reuse first polygon, it's the second one - aCombinedPolyPoly.remove(0); - - for(size_t a(0); a < rEntries.size() - 1; a++) + else { - // create next inner polygon, combined with last one - basegfx::B2DPolygon aNextPoly(rUnitPolygon); - - aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix); - aCombinedPolyPoly.append(aNextPoly); - - // create primitive with correct color - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - aCombinedPolyPoly, - rEntries[a].maBColor)); - - // reuse inner polygon, it's the 2nd one - aCombinedPolyPoly.remove(0); + // NonOverlappingFill + if (getFillGradient().getColorStops().size() < 2) + { + // not really a gradient, we need to create a start primitive + // entry using the single color and the covered area + const basegfx::B2DRange aOutmostRange(getOutputRange()); + aContainer.push_back( + new PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aOutmostRange)), + getOuterColor())); + } + else + { + // gradient with stops, prepare CombinedPolyPoly, use callback + basegfx::B2DPolyPolygon aCombinedPolyPoly; + basegfx::BColor aLastColor; + + auto aCallback([&aContainer,&aCombinedPolyPoly,&aLastColor,this]( + const basegfx::B2DHomMatrix& rMatrix, + const basegfx::BColor& rColor) + { + if (aContainer.empty()) + { + // 1st callback, init CombinedPolyPoly & create 1st entry + basegfx::B2DRange aOutmostRange(getOutputRange()); + + // expand aOutmostRange with transformed first polygon + // to ensure confinement + basegfx::B2DPolygon aFirstPoly(getUnitPolygon()); + aFirstPoly.transform(rMatrix); + aOutmostRange.expand(aFirstPoly.getB2DRange()); + + // build 1st combined polygon; outmost range 1st, then + // the shaped, transformed polygon + aCombinedPolyPoly.append(basegfx::utils::createPolygonFromRect(aOutmostRange)); + aCombinedPolyPoly.append(aFirstPoly); + + // create first primitive + aContainer.push_back( + new PolyPolygonColorPrimitive2D( + aCombinedPolyPoly, + getOuterColor())); + + // save first polygon for re-use in next call, it's the second + // one, so remove 1st + aCombinedPolyPoly.remove(0); + + // remember color for next primitive creation + aLastColor = rColor; + } + else + { + // regular n-th callback, create combined entry by re-using + // CombinedPolyPoly and aLastColor + basegfx::B2DPolygon aNextPoly(getUnitPolygon()); + aNextPoly.transform(rMatrix); + aCombinedPolyPoly.append(aNextPoly); + + // create primitive with correct color + aContainer.push_back( + new PolyPolygonColorPrimitive2D( + aCombinedPolyPoly, + aLastColor)); + + // prepare re-use of inner polygon, save color + aCombinedPolyPoly.remove(0); + aLastColor = rColor; + } + }); + + // call value generator to trigger callbacks + generateMatricesAndColors(aCallback); + + // add last inner polygon with last color + aContainer.push_back( + new PolyPolygonColorPrimitive2D( + std::move(aCombinedPolyPoly), + aLastColor)); + } } - - // add last inner polygon with last color - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - aCombinedPolyPoly, - rEntries[rEntries.size() - 1].maBColor)); + return new GroupPrimitive2D(std::move(aContainer)); } - void FillGradientPrimitive2D::createFill(Primitive2DContainer& rContainer, bool bOverlapping) const + Primitive2DReference FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { - // prepare shape of the Unit Polygon - basegfx::B2DPolygon aUnitPolygon; - - switch(getFillGradient().getStyle()) + // SDPR: support alpha directly now. If a primitive processor + // cannot deal with it, use it's decomposition. For that purpose + // this decomposition has two stages now: This 1st one will + // (if needed) separate content and alpha into a TransparencePrimitive2D + // and (if needed) embed that to a UnifiedTransparencePrimitive2D, + // so all processors can work as before + if (hasAlphaGradient() || hasTransparency()) { - case attribute::GradientStyle::Radial: - case attribute::GradientStyle::Elliptical: + Primitive2DReference aRetval( + new FillGradientPrimitive2D( + getOutputRange(), + getDefinitionRange(), + getFillGradient())); + + if (hasAlphaGradient()) { - aUnitPolygon = basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0); - break; + Primitive2DContainer aAlpha{ new FillGradientPrimitive2D( + getOutputRange(), + getDefinitionRange(), + getAlphaGradient()) }; + + aRetval = new TransparencePrimitive2D(Primitive2DContainer{ aRetval }, std::move(aAlpha)); } - default: // GradientStyle::Linear, attribute::GradientStyle::Axial, attribute::GradientStyle::Square, attribute::GradientStyle::Rect + + if (hasTransparency()) { - aUnitPolygon = basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0)); - break; + aRetval = new UnifiedTransparencePrimitive2D(Primitive2DContainer{ aRetval }, getTransparency()); } - } - // get the transform matrices and colors (where colors - // will have one more entry that matrices) - std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries; - basegfx::BColor aOuterColor; - - generateMatricesAndColors(aEntries, aOuterColor); - - if(bOverlapping) - { - createOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon); + return aRetval; } - else - { - createNonOverlappingFill(rContainer, aEntries, aOuterColor, aUnitPolygon); - } - } - void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const - { // default creates overlapping fill which works with AntiAliasing and without. // The non-overlapping version does not create single filled polygons, but // PolyPolygons where each one describes a 'ring' for the gradient such // that the rings will not overlap. This is useful for the old XOR-paint // 'trick' of VCL which is recorded in Metafiles; so this version may be // used from the MetafilePrimitive2D in its decomposition. - if(!getFillGradient().isDefault()) { - createFill(rContainer, /*bOverlapping*/true); + return createFill(/*bOverlapping*/true); } + + return nullptr; } FillGradientPrimitive2D::FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, const attribute::FillGradientAttribute& rFillGradient) - : BufferedDecompositionPrimitive2D(), - maOutputRange(rOutputRange), - maDefinitionRange(rOutputRange), - maFillGradient(rFillGradient) + : maOutputRange(rOutputRange) + , maDefinitionRange(rOutputRange) + , maFillGradient(rFillGradient) + , maAlphaGradient() + , mfTransparency(0.0) { } FillGradientPrimitive2D::FillGradientPrimitive2D( const basegfx::B2DRange& rOutputRange, const basegfx::B2DRange& rDefinitionRange, - const attribute::FillGradientAttribute& rFillGradient) - : BufferedDecompositionPrimitive2D(), - maOutputRange(rOutputRange), - maDefinitionRange(rDefinitionRange), - maFillGradient(rFillGradient) + const attribute::FillGradientAttribute& rFillGradient, + const attribute::FillGradientAttribute* pAlphaGradient, + double fTransparency) + : maOutputRange(rOutputRange) + , maDefinitionRange(rDefinitionRange) + , maFillGradient(rFillGradient) + , maAlphaGradient() + , mfTransparency(fTransparency) { + // copy alpha gradient if we got one + if (nullptr != pAlphaGradient) + maAlphaGradient = *pAlphaGradient; } bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive); + if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + return false; - return (getOutputRange() == rCompare.getOutputRange() - && getDefinitionRange() == rCompare.getDefinitionRange() - && getFillGradient() == rCompare.getFillGradient()); - } + const FillGradientPrimitive2D& rCompare(static_cast<const FillGradientPrimitive2D&>(rPrimitive)); + + if (getOutputRange() != rCompare.getOutputRange()) + return false; + + if (getDefinitionRange() != rCompare.getDefinitionRange()) + return false; + + if (getFillGradient() != rCompare.getFillGradient()) + return false; - return false; + if (maAlphaGradient != rCompare.maAlphaGradient) + return false; + + if (!basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())) + return false; + + return true; } basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const @@ -326,7 +368,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D) + sal_uInt32 FillGradientPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx index 708b3f2ce48d..5ef71c97aa33 100644 --- a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx @@ -24,7 +24,9 @@ #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <primitive2d/graphicprimitivehelper2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitivehelper2d.hxx> +#include <utility> +#include <vcl/graph.hxx> using namespace com::sun::star; @@ -32,24 +34,25 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void FillGraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference FillGraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { const attribute::FillGraphicAttribute& rAttribute = getFillGraphic(); if(rAttribute.isDefault()) - return; + return nullptr; const Graphic& rGraphic = rAttribute.getGraphic(); if(GraphicType::Bitmap != rGraphic.GetType() && GraphicType::GdiMetafile != rGraphic.GetType()) - return; + return nullptr; const Size aSize(rGraphic.GetPrefSize()); if(!(aSize.Width() && aSize.Height())) - return; + return nullptr; // we have a graphic (bitmap or metafile) with some size + Primitive2DContainer aContainer; if(rAttribute.getTiling()) { // get object range and create tiling matrices @@ -66,13 +69,15 @@ namespace drawinglayer::primitive2d Primitive2DContainer xSeq; create2DDecompositionOfGraphic(xSeq, rGraphic, - basegfx::B2DHomMatrix()); + basegfx::B2DHomMatrix(), + getTransparency()); - for(size_t a(0); a < aMatrices.size(); a++) + rtl::Reference<GroupPrimitive2D> xGroup = new GroupPrimitive2D(std::move(xSeq)); + for(const auto &a : aMatrices) { - rContainer.push_back(new TransformPrimitive2D( - getTransformation() * aMatrices[a], - xSeq)); + aContainer.push_back(new TransformPrimitive2D( + getTransformation() * a, + *xGroup)); } } else @@ -83,18 +88,23 @@ namespace drawinglayer::primitive2d rAttribute.getGraphicRange().getRange(), rAttribute.getGraphicRange().getMinimum())); - create2DDecompositionOfGraphic(rContainer, + create2DDecompositionOfGraphic(aContainer, rGraphic, - aObjectTransform); + aObjectTransform, + getTransparency()); } + + return new GroupPrimitive2D(std::move(aContainer)); } FillGraphicPrimitive2D::FillGraphicPrimitive2D( - const basegfx::B2DHomMatrix& rTransformation, - const attribute::FillGraphicAttribute& rFillGraphic) - : BufferedDecompositionPrimitive2D(), - maTransformation(rTransformation), - maFillGraphic(rFillGraphic) + basegfx::B2DHomMatrix aTransformation, + const attribute::FillGraphicAttribute& rFillGraphic, + double fTransparency) + : maTransformation(std::move(aTransformation)) + , maFillGraphic(rFillGraphic) + , maOffsetXYCreatedBitmap() + , mfTransparency(std::max(0.0, std::min(1.0, fTransparency))) { } @@ -105,7 +115,8 @@ namespace drawinglayer::primitive2d const FillGraphicPrimitive2D& rCompare = static_cast< const FillGraphicPrimitive2D& >(rPrimitive); return (getTransformation() == rCompare.getTransformation() - && getFillGraphic() == rCompare.getFillGraphic()); + && getFillGraphic() == rCompare.getFillGraphic() + && basegfx::fTools::equal(getTransparency(), rCompare.getTransparency())); } return false; @@ -121,7 +132,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(FillGraphicPrimitive2D, PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D) + sal_uInt32 FillGraphicPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx index 9c4ef03bfba3..c855460824e7 100644 --- a/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/fillhatchprimitive2d.cxx @@ -19,12 +19,14 @@ #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> #include <texture/texture.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolygon.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> +#include <utility> using namespace com::sun::star; @@ -32,10 +34,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void FillHatchPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference FillHatchPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { if(getFillHatch().isDefault()) - return; + return nullptr; // create hatch const basegfx::BColor aHatchColor(getFillHatch().getColor()); @@ -65,7 +67,7 @@ namespace drawinglayer::primitive2d getDefinitionRange(), getOutputRange(), fDistance, - fAngle - F_PI4); + fAngle - M_PI_4); aHatch.appendTransformations(aMatrices); @@ -78,7 +80,7 @@ namespace drawinglayer::primitive2d getDefinitionRange(), getOutputRange(), fDistance, - fAngle - F_PI2); + fAngle - M_PI_2); aHatch.appendTransformations(aMatrices); @@ -99,12 +101,12 @@ namespace drawinglayer::primitive2d // prepare return value const bool bFillBackground(getFillHatch().isFillBackground()); - + Primitive2DContainer aContainer; // evtl. create filled background if(bFillBackground) { // create primitive for background - rContainer.push_back( + aContainer.push_back( new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon( basegfx::utils::createPolygonFromRect(getOutputRange())), getBColor())); @@ -114,27 +116,27 @@ namespace drawinglayer::primitive2d const basegfx::B2DPoint aStart(0.0, 0.0); const basegfx::B2DPoint aEnd(1.0, 0.0); - for(size_t a(0); a < aMatrices.size(); a++) + for (const auto &a : aMatrices) { - const basegfx::B2DHomMatrix& rMatrix = aMatrices[a]; + const basegfx::B2DHomMatrix& rMatrix = a; basegfx::B2DPolygon aNewLine; aNewLine.append(rMatrix * aStart); aNewLine.append(rMatrix * aEnd); // create hairline - rContainer.push_back(new PolygonHairlinePrimitive2D(aNewLine, aHatchColor)); + aContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aNewLine), aHatchColor)); } + return new GroupPrimitive2D(std::move(aContainer)); } FillHatchPrimitive2D::FillHatchPrimitive2D( const basegfx::B2DRange& rOutputRange, const basegfx::BColor& rBColor, - const attribute::FillHatchAttribute& rFillHatch) - : DiscreteMetricDependentPrimitive2D(), - maOutputRange(rOutputRange), + attribute::FillHatchAttribute aFillHatch) + : maOutputRange(rOutputRange), maDefinitionRange(rOutputRange), - maFillHatch(rFillHatch), + maFillHatch(std::move(aFillHatch)), maBColor(rBColor) { } @@ -143,11 +145,10 @@ namespace drawinglayer::primitive2d const basegfx::B2DRange& rOutputRange, const basegfx::B2DRange& rDefinitionRange, const basegfx::BColor& rBColor, - const attribute::FillHatchAttribute& rFillHatch) - : DiscreteMetricDependentPrimitive2D(), - maOutputRange(rOutputRange), + attribute::FillHatchAttribute aFillHatch) + : maOutputRange(rOutputRange), maDefinitionRange(rDefinitionRange), - maFillHatch(rFillHatch), + maFillHatch(std::move(aFillHatch)), maBColor(rBColor) { } @@ -175,7 +176,6 @@ namespace drawinglayer::primitive2d void FillHatchPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); bool bAdaptDistance(0 != getFillHatch().getMinimalDiscreteDistance()); if(bAdaptDistance) @@ -191,7 +191,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(FillHatchPrimitive2D, PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D) + sal_uInt32 FillHatchPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/glowprimitive2d.cxx b/drawinglayer/source/primitive2d/glowprimitive2d.cxx index fb6a599ab4e2..5f565c28341f 100644 --- a/drawinglayer/source/primitive2d/glowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/glowprimitive2d.cxx @@ -18,29 +18,39 @@ */ #include <drawinglayer/primitive2d/glowprimitive2d.hxx> -#include <basegfx/color/bcolormodifier.hxx> -#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <drawinglayer/converters.hxx> +#include "GlowSoftEgdeShadowTools.hxx" -#include <sal/log.hxx> -#include <memory> +#ifdef DBG_UTIL +#include <o3tl/environment.hxx> +#include <tools/stream.hxx> +#include <vcl/filter/PngImageWriter.hxx> +#endif using namespace com::sun::star; namespace drawinglayer::primitive2d { GlowPrimitive2D::GlowPrimitive2D(const Color& rGlowColor, double fRadius, - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + Primitive2DContainer&& rChildren) + : BufferedDecompositionGroupPrimitive2D(std::move(rChildren)) , maGlowColor(rGlowColor) , mfGlowRadius(fRadius) + , mfLastDiscreteGlowRadius(0.0) + , maLastClippedRange() { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if (BasePrimitive2D::operator==(rPrimitive)) + if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive)) { const GlowPrimitive2D& rCompare = static_cast<const GlowPrimitive2D&>(rPrimitive); @@ -51,17 +61,298 @@ bool GlowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const return false; } +bool GlowPrimitive2D::prepareValuesAndcheckValidity( + basegfx::B2DRange& rGlowRange, basegfx::B2DRange& rClippedRange, + basegfx::B2DVector& rDiscreteGlowSize, double& rfDiscreteGlowRadius, + const geometry::ViewInformation2D& rViewInformation) const +{ + // no GlowRadius defined, done + if (getGlowRadius() <= 0.0) + return false; + + // no geometry, done + if (getChildren().empty()) + return false; + + // no pixel target, done + if (rViewInformation.getObjectToViewTransformation().isIdentity()) + return false; + + // get geometry range that defines area that needs to be pixelated + rGlowRange = getChildren().getB2DRange(rViewInformation); + + // no range of geometry, done + if (rGlowRange.isEmpty()) + return false; + + // extend range by GlowRadius in all directions + rGlowRange.grow(getGlowRadius()); + + // initialize ClippedRange to full GlowRange -> all is visible + rClippedRange = rGlowRange; + + // get Viewport and check if used. If empty, all is visible (see + // ViewInformation2D definition in viewinformation2d.hxx) + if (!rViewInformation.getViewport().isEmpty()) + { + // if used, extend by GlowRadius to ensure needed parts are included + basegfx::B2DRange aVisibleArea(rViewInformation.getViewport()); + aVisibleArea.grow(getGlowRadius()); + + // To do this correctly, it needs to be done in discrete coordinates. + // The object may be transformed relative to the original# + // ObjectTransformation, e.g. when re-used in shadow + aVisibleArea.transform(rViewInformation.getViewTransformation()); + rClippedRange.transform(rViewInformation.getObjectToViewTransformation()); + + // calculate ClippedRange + rClippedRange.intersect(aVisibleArea); + + // if GlowRange is completely outside of VisibleArea, ClippedRange + // will be empty and we are done + if (rClippedRange.isEmpty()) + return false; + + // convert result back to object coordinates + rClippedRange.transform(rViewInformation.getInverseObjectToViewTransformation()); + } + + // calculate discrete pixel size of GlowRange. If it's too small to visualize, we are done + rDiscreteGlowSize = rViewInformation.getObjectToViewTransformation() * rGlowRange.getRange(); + if (ceil(rDiscreteGlowSize.getX()) < 2.0 || ceil(rDiscreteGlowSize.getY()) < 2.0) + return false; + + // calculate discrete pixel size of GlowRadius. If it's too small to visualize, we are done + rfDiscreteGlowRadius = ceil( + (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getGlowRadius(), 0)) + .getLength()); + if (rfDiscreteGlowRadius < 1.0) + return false; + + return true; +} + +void GlowPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DRange aGlowRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteGlowSize; + double fDiscreteGlowRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aGlowRange, aClippedRange, aDiscreteGlowSize, + fDiscreteGlowRadius, rViewInformation)) + return; + + // Create embedding transformation from object to top-left zero-aligned + // target pixel geometry (discrete form of ClippedRange) + // First, move to top-left of GlowRange + const sal_uInt32 nDiscreteGlowWidth(ceil(aDiscreteGlowSize.getX())); + const sal_uInt32 nDiscreteGlowHeight(ceil(aDiscreteGlowSize.getY())); + basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix( + -aClippedRange.getMinX(), -aClippedRange.getMinY())); + // Second, scale to discrete bitmap size + // Even when using the offset from ClippedRange, we need to use the + // scaling from the full representation, thus from GlowRange + aEmbedding.scale(nDiscreteGlowWidth / aGlowRange.getWidth(), + nDiscreteGlowHeight / aGlowRange.getHeight()); + + // Embed content graphics to TransformPrimitive2D + const primitive2d::Primitive2DReference xEmbedRef( + new primitive2d::TransformPrimitive2D(aEmbedding, Primitive2DContainer(getChildren()))); + primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef }; + + // Create Bitmap using drawinglayer tooling, including a MaximumQuadraticPixel + // limitation to be safe and not go runtime/memory havoc. Use a pretty small + // limit due to this is glow functionality and will look good with bitmap scaling + // anyways. The value of 250.000 square pixels below maybe adapted as needed. + const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation() + * aClippedRange.getRange()); + const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX())); + const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY())); + const geometry::ViewInformation2D aViewInformation2D; + const sal_uInt32 nMaximumQuadraticPixels(250000); + + // I have now added a helper that just creates the mask without having + // to render the content, use it, it's faster + const AlphaMask aAlpha(::drawinglayer::createAlphaMask( + std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight, + nMaximumQuadraticPixels)); + + if (aAlpha.IsEmpty()) + return; + + const Size aBitmapExSizePixel(aAlpha.GetSizePixel()); + + if (aBitmapExSizePixel.Width() <= 0 || aBitmapExSizePixel.Height() <= 0) + return; + + // We may have to take a corrective scaling into account when the + // MaximumQuadraticPixel limit was used/triggered + double fScale(1.0); + + if (static_cast<sal_uInt32>(aBitmapExSizePixel.Width()) != nDiscreteClippedWidth + || static_cast<sal_uInt32>(aBitmapExSizePixel.Height()) != nDiscreteClippedHeight) + { + // scale in X and Y should be the same (see fReduceFactor in createAlphaMask), + // so adapt numerically to a single scale value, they are integer rounded values + const double fScaleX(static_cast<double>(aBitmapExSizePixel.Width()) + / static_cast<double>(nDiscreteClippedWidth)); + const double fScaleY(static_cast<double>(aBitmapExSizePixel.Height()) + / static_cast<double>(nDiscreteClippedHeight)); + + fScale = (fScaleX + fScaleY) * 0.5; + } + + // fDiscreteGlowRadius is the size of the halo from each side of the object. The halo is the + // border of glow color that fades from glow transparency level to fully transparent + // When blurring a sharp boundary (our case), it gets 50% of original intensity, and + // fades to both sides by the blur radius; thus blur radius is half of glow radius. + // Consider glow transparency (initial transparency near the object edge) + AlphaMask mask(ProcessAndBlurAlphaMask(aAlpha, fDiscreteGlowRadius * fScale / 2.0, + fDiscreteGlowRadius * fScale / 2.0, + 255 - getGlowColor().GetAlpha())); + + // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask + Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP); + bmp.Erase(getGlowColor()); + Bitmap result(bmp, mask); + +#ifdef DBG_UTIL + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + if (bDoSaveForVisualControl) + { + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) + { + SvFileStream aNew(sDumpPath + "test_glow.png", StreamMode::WRITE | StreamMode::TRUNC); + vcl::PngImageWriter aPNGWriter(aNew); + aPNGWriter.write(result); + } + } +#endif + + // Independent from discrete sizes of glow alpha creation, always + // map and project glow result to geometry range extended by glow + // radius, but to the eventually clipped instance (ClippedRange) + const primitive2d::Primitive2DReference xEmbedRefBitmap( + new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix( + aClippedRange.getWidth(), aClippedRange.getHeight(), + aClippedRange.getMinX(), aClippedRange.getMinY()))); + + rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap }; +} + +// Using tooling class BufferedDecompositionGroupPrimitive2D now, so +// no more need to locally do the buffered get2DDecomposition here, +// see BufferedDecompositionGroupPrimitive2D::get2DDecomposition +void GlowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DRange aGlowRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteGlowSize; + double fDiscreteGlowRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aGlowRange, aClippedRange, aDiscreteGlowSize, + fDiscreteGlowRadius, rViewInformation)) + return; + + if (hasBuffered2DDecomposition()) + { + // First check is to detect if the last created decompose is capable + // to represent the now requested visualization. + // ClippedRange is the needed visualizationArea for the current glow + // effect, LastClippedRange is the one from the existing/last rendering. + // Check if last created area is sufficient and can be re-used + if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange)) + { + // To avoid unnecessary invalidations due to being *very* correct + // with HairLines (which are view-dependent and thus change the + // result(s) here slightly when changing zoom), add a slight unsharp + // component if we have a ViewTransform. The derivation is inside + // the range of half a pixel (due to one pixel hairline) + basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange); + + if (!rViewInformation.getObjectToViewTransformation().isIdentity()) + { + // Grow by view-dependent size of 1/2 pixel + const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation() + * basegfx::B2DVector(0.5, 0)) + .getLength()); + aLastClippedRangeAndHairline.grow(fHalfPixel); + } + + if (!aLastClippedRangeAndHairline.isInside(aClippedRange)) + { + // Conditions of last local decomposition have changed, delete + const_cast<GlowPrimitive2D*>(this)->setBuffered2DDecomposition( + Primitive2DContainer()); + } + } + } + + if (hasBuffered2DDecomposition()) + { + // Second check is to react on changes of the DiscreteGlowRadius when + // zooming in/out. + // Use the known last and current DiscreteGlowRadius to decide + // if the visualization can be re-used. Be a little 'creative' here + // and make it dependent on a *relative* change - it is not necessary + // to re-create everytime if the exact value is missed since zooming + // pixel-based glow effect is pretty good due to it's smooth nature + bool bFree(mfLastDiscreteGlowRadius <= 0.0 || fDiscreteGlowRadius <= 0.0); + + if (!bFree) + { + const double fDiff(fabs(mfLastDiscreteGlowRadius - fDiscreteGlowRadius)); + const double fLen(fabs(mfLastDiscreteGlowRadius) + fabs(fDiscreteGlowRadius)); + const double fRelativeChange(fDiff / fLen); + + // Use lower fixed values here to change more often, higher to change less often. + // Value is in the range of ]0.0 .. 1.0] + bFree = fRelativeChange >= 0.15; + } + + if (bFree) + { + // Conditions of last local decomposition have changed, delete + const_cast<GlowPrimitive2D*>(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + } + + if (!hasBuffered2DDecomposition()) + { + // refresh last used DiscreteGlowRadius and ClippedRange to new remembered values + const_cast<GlowPrimitive2D*>(this)->mfLastDiscreteGlowRadius = fDiscreteGlowRadius; + const_cast<GlowPrimitive2D*>(this)->maLastClippedRange = aClippedRange; + } + + // call parent, that will check for empty, call create2DDecomposition and + // set as decomposition + BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + basegfx::B2DRange GlowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const { - basegfx::B2DRange aRetval(GroupPrimitive2D::getB2DRange(rViewInformation)); + // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily) + // use the decompose - what works, but is not needed here. + // We know the to-be-visualized geometry and the radius it needs to be extended, + // so simply calculate the exact needed range. + basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation)); + // We need additional space for the glow from all sides aRetval.grow(getGlowRadius()); + return aRetval; } // provide unique ID -ImplPrimitive2DIDBlock(GlowPrimitive2D, PRIMITIVE2D_ID_GLOWPRIMITIVE2D) +sal_uInt32 GlowPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_GLOWPRIMITIVE2D; } } // end of namespace diff --git a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx index f86b1585b13f..0f0d3771867f 100644 --- a/drawinglayer/source/primitive2d/graphicprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/graphicprimitive2d.cxx @@ -24,19 +24,20 @@ #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> #include <primitive2d/cropprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <primitive2d/graphicprimitivehelper2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitivehelper2d.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <utility> namespace drawinglayer::primitive2d { -void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, - const geometry::ViewInformation2D&) const +Primitive2DReference +GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&) const { - if (255 == getGraphicAttr().GetTransparency()) + if (0 == getGraphicAttr().GetAlpha()) { // content is invisible, done - return; + return nullptr; } // do not apply mirroring from GraphicAttr to the Metafile by calling @@ -68,45 +69,60 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, GraphicAttr aSuppressGraphicAttr(getGraphicAttr()); aSuppressGraphicAttr.SetCrop(0, 0, 0, 0); - aSuppressGraphicAttr.SetRotation(0); + aSuppressGraphicAttr.SetRotation(0_deg10); aSuppressGraphicAttr.SetMirrorFlags(BmpMirrorFlags::NONE); - aSuppressGraphicAttr.SetTransparency(0); + aSuppressGraphicAttr.SetAlpha(255); const GraphicObject& rGraphicObject = getGraphicObject(); Graphic aTransformedGraphic(rGraphicObject.GetGraphic()); - const bool isBitmap(GraphicType::Bitmap == aTransformedGraphic.GetType() - && !aTransformedGraphic.getVectorGraphicData()); const bool isAdjusted(getGraphicAttr().IsAdjusted()); const bool isDrawMode(GraphicDrawMode::Standard != getGraphicAttr().GetDrawMode()); - if (isBitmap && (isAdjusted || isDrawMode)) + // I have now added buffering BColorModifierStack-adapted Bitmaps, + // see Bitmap::Modify, thus the primitive case is fast now. + // It buffers the adapted bitmap and at that the SDPRs can then buffer + // the system-dependent representation. + // I keep the code below (adding a static switch). It modifies the + // Graphic and is a reliable fallback - just in case. Remember that + // it does *not* buffer and has to modify again at each re-use... + static bool bUseOldModification(false); + + if (bUseOldModification) { - // the pure primitive solution with the color modifiers works well, too, but when - // it is a bitmap graphic the old modification currently is faster; so use it here - // instead of creating all as in create2DColorModifierEmbeddingsAsNeeded (see below). - // Still, crop, rotation, mirroring and transparency is handled by primitives already - // (see above). - // This could even be done when vector graphic, but we explicitly want to have the - // pure primitive solution for this; this will allow vector graphics to stay vector - // graphics, independent from the color filtering stuff. This will enhance e.g. - // SVG and print quality while reducing data size at the same time. - // The other way around the old modifications when only used on already bitmap objects - // will not lose any quality. - aTransformedGraphic = rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr); - - // reset GraphicAttr after use to not apply double - aSuppressGraphicAttr = GraphicAttr(); + const bool isBitmap(GraphicType::Bitmap == aTransformedGraphic.GetType() + && !aTransformedGraphic.getVectorGraphicData()); + + if (isBitmap && (isAdjusted || isDrawMode)) + { + // the pure primitive solution with the color modifiers works well, too, but when + // it is a bitmap graphic the old modification currently is faster; so use it here + // instead of creating all as in create2DColorModifierEmbeddingsAsNeeded (see below). + // Still, crop, rotation, mirroring and transparency is handled by primitives already + // (see above). + // This could even be done when vector graphic, but we explicitly want to have the + // pure primitive solution for this; this will allow vector graphics to stay vector + // graphics, independent from the color filtering stuff. This will enhance e.g. + // SVG and print quality while reducing data size at the same time. + // The other way around the old modifications when only used on already bitmap objects + // will not lose any quality. + aTransformedGraphic = rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr); + + // reset GraphicAttr after use to not apply double + aSuppressGraphicAttr = GraphicAttr(); + } } // create sub-content; helper takes care of correct handling of - // bitmap, svg or metafile content + // bitmap, svg or metafile content. also handle alpha there directly Primitive2DContainer aRetval; - create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform); + const double fTransparency( + std::clamp((255 - getGraphicAttr().GetAlpha()) * (1.0 / 255.0), 0.0, 1.0)); + create2DDecompositionOfGraphic(aRetval, aTransformedGraphic, aTransform, fTransparency); if (aRetval.empty()) { // content is invisible, done - return; + return nullptr; } if (isAdjusted || isDrawMode) @@ -114,7 +130,7 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, // embed to needed ModifiedColorPrimitive2D's if necessary. Do this for // adjustments and draw mode specials aRetval = create2DColorModifierEmbeddingsAsNeeded( - aRetval, aSuppressGraphicAttr.GetDrawMode(), + std::move(aRetval), aSuppressGraphicAttr.GetDrawMode(), std::clamp(aSuppressGraphicAttr.GetLuminance() * 0.01, -1.0, 1.0), std::clamp(aSuppressGraphicAttr.GetContrast() * 0.01, -1.0, 1.0), std::clamp(aSuppressGraphicAttr.GetChannelR() * 0.01, -1.0, 1.0), @@ -126,22 +142,7 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, if (aRetval.empty()) { // content is invisible, done - return; - } - } - - if (getGraphicAttr().IsTransparent()) - { - // check for transparency - const double fTransparency( - std::clamp(getGraphicAttr().GetTransparency() * (1.0 / 255.0), 0.0, 1.0)); - - if (!basegfx::fTools::equalZero(fTransparency)) - { - const Primitive2DReference aUnifiedTransparence( - new UnifiedTransparencePrimitive2D(aRetval, fTransparency)); - - aRetval = Primitive2DContainer{ aUnifiedTransparence }; + return nullptr; } } @@ -157,35 +158,35 @@ void GraphicPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, getGraphicAttr().GetBottomCrop())); // embed content in cropPrimitive - Primitive2DReference xPrimitive(new CropPrimitive2D( - aRetval, aTransform, getGraphicAttr().GetLeftCrop() * aCropScaleFactor.getX(), + aRetval = Primitive2DContainer{ new CropPrimitive2D( + std::move(aRetval), aTransform, + getGraphicAttr().GetLeftCrop() * aCropScaleFactor.getX(), getGraphicAttr().GetTopCrop() * aCropScaleFactor.getY(), getGraphicAttr().GetRightCrop() * aCropScaleFactor.getX(), - getGraphicAttr().GetBottomCrop() * aCropScaleFactor.getY())); - - aRetval = Primitive2DContainer{ xPrimitive }; + getGraphicAttr().GetBottomCrop() * aCropScaleFactor.getY()) }; } - rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); + return new GroupPrimitive2D(std::move(aRetval)); } -GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform, +GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform, const GraphicObject& rGraphicObject, const GraphicAttr& rGraphicAttr) - : BufferedDecompositionPrimitive2D() - , maTransform(rTransform) + : maTransform(std::move(aTransform)) , maGraphicObject(rGraphicObject) , maGraphicAttr(rGraphicAttr) { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } -GraphicPrimitive2D::GraphicPrimitive2D(const basegfx::B2DHomMatrix& rTransform, +GraphicPrimitive2D::GraphicPrimitive2D(basegfx::B2DHomMatrix aTransform, const GraphicObject& rGraphicObject) - : BufferedDecompositionPrimitive2D() - , maTransform(rTransform) + : maTransform(std::move(aTransform)) , maGraphicObject(rGraphicObject) - , maGraphicAttr() { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const @@ -211,7 +212,10 @@ GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInform } // provide unique ID -ImplPrimitive2DIDBlock(GraphicPrimitive2D, PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) +sal_uInt32 GraphicPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D; +} } // end of namespace diff --git a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx index 9c441dc0d876..092c7b86dcdf 100644 --- a/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx +++ b/drawinglayer/source/primitive2d/graphicprimitivehelper2d.cxx @@ -21,14 +21,18 @@ #include <algorithm> -#include <primitive2d/graphicprimitivehelper2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitivehelper2d.hxx> #include <drawinglayer/animation/animationtiming.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/animatedprimitive2d.hxx> #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -36,7 +40,9 @@ // helper class for animated graphics +#include <utility> #include <vcl/animate/Animation.hxx> +#include <vcl/alpha.hxx> #include <vcl/graph.hxx> #include <vcl/virdev.hxx> #include <vcl/svapp.hxx> @@ -58,11 +64,20 @@ namespace drawinglayer::primitive2d an instance of Graphic is used here since it's ref-counted and thus a safe copy for now */ - const Graphic maGraphic; + Graphic maGraphic; + + /** defines parameters for tiling if this AnimatedGraphicPrimitive2D + is to be used for a FillGraphicPrimitive2D. In that case, + maFillGraphicAttribute.isDefault() will be false + */ + drawinglayer::attribute::FillGraphicAttribute maFillGraphicAttribute; /// local animation processing data, excerpt from maGraphic ::Animation maAnimation; + /// the transparency in range [0.0 .. 1.0] + double mfTransparency; + /// the on-demand created VirtualDevices for frame creation ScopedVclPtrInstance< VirtualDevice > maVirtualDevice; ScopedVclPtrInstance< VirtualDevice > maVirtualDeviceMask; @@ -74,7 +89,7 @@ namespace drawinglayer::primitive2d Primitive2DReference maBufferedFirstFrame; /// buffering of all frames - Primitive2DContainer maBufferedPrimitives; + std::vector<Primitive2DReference> maBufferedPrimitives; bool mbBufferingAllowed; /// set if the animation is huge so that just always the next frame @@ -86,7 +101,8 @@ namespace drawinglayer::primitive2d { return (GraphicType::Bitmap == maGraphic.GetType() && maGraphic.IsAnimated() - && maAnimation.Count()); + && maAnimation.Count() + && !basegfx::fTools::equal(getTransparency(), 1.0)); } void ensureVirtualDeviceSizeAndState() @@ -103,6 +119,10 @@ namespace drawinglayer::primitive2d maVirtualDeviceMask->EnableMapMode(false); maVirtualDevice->SetOutputSizePixel(aTarget); maVirtualDeviceMask->SetOutputSizePixel(aTarget); + + // tdf#156630 make erase calls fill with transparency + maVirtualDevice->SetBackground(COL_BLACK); + maVirtualDeviceMask->SetBackground(COL_ALPHA_TRANSPARENT); } maVirtualDevice->Erase(); @@ -115,13 +135,13 @@ namespace drawinglayer::primitive2d sal_uInt32 generateStepTime(sal_uInt32 nIndex) const { - const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(nIndex)); - sal_uInt32 nWaitTime(rAnimationBitmap.mnWait * 10); + const AnimationFrame& rAnimationFrame = maAnimation.Get(sal_uInt16(nIndex)); + sal_uInt32 nWaitTime(rAnimationFrame.mnWait * 10); // Take care of special value for MultiPage TIFFs. ATM these shall just // show their first page. Later we will offer some switching when object // is selected. - if (ANIMATION_TIMEOUT_ON_CLICK == rAnimationBitmap.mnWait) + if (ANIMATION_TIMEOUT_ON_CLICK == rAnimationFrame.mnWait) { // ATM the huge value would block the timer, so // use a long time to show first page (whole day) @@ -162,7 +182,7 @@ namespace drawinglayer::primitive2d Primitive2DReference createFromBuffer() const { - // create BitmapEx by extracting from VirtualDevices + // create Bitmap by extracting from VirtualDevices const Bitmap aMainBitmap(maVirtualDevice->GetBitmap(Point(), maVirtualDevice->GetOutputSizePixel())); bool useAlphaMask = false; #if defined(MACOSX) || defined(IOS) @@ -172,22 +192,41 @@ namespace drawinglayer::primitive2d if( SkiaHelper::isVCLSkiaEnabled()) useAlphaMask = true; #endif - BitmapEx bitmap; + Bitmap bitmap; if( useAlphaMask ) { const AlphaMask aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel())); - bitmap = BitmapEx(aMainBitmap, aMaskBitmap); + bitmap = Bitmap(aMainBitmap, aMaskBitmap); } else { - const Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel())); - bitmap = BitmapEx(aMainBitmap, aMaskBitmap); + Bitmap aMaskBitmap(maVirtualDeviceMask->GetBitmap(Point(), maVirtualDeviceMask->GetOutputSizePixel())); + // tdf#156630 invert the alpha mask + aMaskBitmap.Invert(); // convert from transparency to alpha + bitmap = Bitmap(aMainBitmap, aMaskBitmap); + } + + if (!maFillGraphicAttribute.isDefault()) + { + // need to create FillGraphicPrimitive2D + const drawinglayer::attribute::FillGraphicAttribute aAttribute( + Graphic(bitmap), + maFillGraphicAttribute.getGraphicRange(), + maFillGraphicAttribute.getTiling(), + maFillGraphicAttribute.getOffsetX(), + maFillGraphicAttribute.getOffsetY()); + + return new FillGraphicPrimitive2D( + getTransform(), + aAttribute, + getTransparency()); } - return Primitive2DReference( - new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(bitmap), - getTransform())); + // need to create BitmapAlphaPrimitive2D/BitmapPrimitive2D + if (basegfx::fTools::equal(getTransparency(), 0.0)) + return new BitmapPrimitive2D(bitmap, getTransform()); + + return new BitmapAlphaPrimitive2D(bitmap, getTransform(), getTransparency()); } void checkSafeToBuffer(sal_uInt32 nIndex) @@ -249,16 +288,24 @@ namespace drawinglayer::primitive2d while (mnNextFrameToPrepare <= nTarget) { // prepare step - const AnimationBitmap& rAnimationBitmap = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare)); + const AnimationFrame& rAnimationFrame = maAnimation.Get(sal_uInt16(mnNextFrameToPrepare)); + + bool bSourceBlending = rAnimationFrame.meBlend == Blend::Source; + + if (bSourceBlending) + { + tools::Rectangle aArea(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmap.GetSizePixel()); + maVirtualDevice->Erase(aArea); + maVirtualDeviceMask->Erase(aArea); + } - switch (rAnimationBitmap.meDisposal) + switch (rAnimationFrame.meDisposal) { case Disposal::Not: { - maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx); - Bitmap aMask = rAnimationBitmap.maBitmapEx.GetMask(); + maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmap); - if (aMask.IsEmpty()) + if (!rAnimationFrame.maBitmap.HasAlpha()) { const Point aEmpty; const ::tools::Rectangle aRect(aEmpty, maVirtualDeviceMask->GetOutputSizePixel()); @@ -267,8 +314,9 @@ namespace drawinglayer::primitive2d } else { - BitmapEx aExpandVisibilityMask(aMask, aMask); - maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask); + AlphaMask aAlphaMask = rAnimationFrame.maBitmap.CreateAlphaMask(); + Bitmap aExpandVisibilityMask(aAlphaMask.GetBitmap(), aAlphaMask); + maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask); } break; @@ -276,32 +324,31 @@ namespace drawinglayer::primitive2d case Disposal::Back: { // #i70772# react on no mask, for primitives, too. - const Bitmap & rMask(rAnimationBitmap.maBitmapEx.GetMask()); - const Bitmap & rContent(rAnimationBitmap.maBitmapEx.GetBitmap()); maVirtualDeviceMask->Erase(); - maVirtualDevice->DrawBitmap(rAnimationBitmap.maPositionPixel, rContent); + maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmap); - if (rMask.IsEmpty()) + if (!rAnimationFrame.maBitmap.HasAlpha()) { - const ::tools::Rectangle aRect(rAnimationBitmap.maPositionPixel, rContent.GetSizePixel()); + const ::tools::Rectangle aRect(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmap.GetSizePixel()); maVirtualDeviceMask->SetFillColor(COL_BLACK); maVirtualDeviceMask->SetLineColor(); maVirtualDeviceMask->DrawRect(aRect); } else { - BitmapEx aExpandVisibilityMask(rMask, rMask); - maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask); + const AlphaMask aMask(rAnimationFrame.maBitmap.CreateAlphaMask()); + Bitmap aExpandVisibilityMask(aMask.GetBitmap(), aMask); + maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask); } break; } case Disposal::Previous: { - maVirtualDevice->DrawBitmapEx(rAnimationBitmap.maPositionPixel, rAnimationBitmap.maBitmapEx); - BitmapEx aExpandVisibilityMask(rAnimationBitmap.maBitmapEx.GetMask(), rAnimationBitmap.maBitmapEx.GetMask()); - maVirtualDeviceMask->DrawBitmapEx(rAnimationBitmap.maPositionPixel, aExpandVisibilityMask); + maVirtualDevice->DrawBitmapEx(rAnimationFrame.maPositionPixel, rAnimationFrame.maBitmap); + Bitmap aExpandVisibilityMask(rAnimationFrame.maBitmap.CreateAlphaMask().GetBitmap(), rAnimationFrame.maBitmap.CreateAlphaMask()); + maVirtualDeviceMask->DrawBitmapEx(rAnimationFrame.maPositionPixel, aExpandVisibilityMask); break; } } @@ -342,38 +389,54 @@ namespace drawinglayer::primitive2d /// constructor AnimatedGraphicPrimitive2D( const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform); + const drawinglayer::attribute::FillGraphicAttribute* pFillGraphicAttribute, + basegfx::B2DHomMatrix aTransform, + double fTransparency = 0.0); + virtual ~AnimatedGraphicPrimitive2D(); /// data read access const basegfx::B2DHomMatrix& getTransform() const { return maTransform; } + double getTransparency() const { return mfTransparency; } + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override { return PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D; } /// compare operator virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; /// override to deliver the correct expected frame dependent of timing virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override; + + /// get range + virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; }; } AnimatedGraphicPrimitive2D::AnimatedGraphicPrimitive2D( const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform) + const drawinglayer::attribute::FillGraphicAttribute* pFillGraphicAttribute, + basegfx::B2DHomMatrix aTransform, + double fTransparency) : AnimatedSwitchPrimitive2D( animation::AnimationEntryList(), Primitive2DContainer(), false), - maTransform(rTransform), + maTransform(std::move(aTransform)), maGraphic(rGraphic), + maFillGraphicAttribute(), maAnimation(rGraphic.GetAnimation()), + mfTransparency(std::max(0.0, std::min(1.0, fTransparency))), maVirtualDevice(*Application::GetDefaultDevice()), - maVirtualDeviceMask(*Application::GetDefaultDevice(), DeviceFormat::BITMASK), + maVirtualDeviceMask(*Application::GetDefaultDevice()), mnNextFrameToPrepare(SAL_MAX_UINT32), - maBufferedFirstFrame(), - maBufferedPrimitives(), mbBufferingAllowed(false), mbHugeSize(false) { + // if FillGraphicAttribute copy it -> FillGraphicPrimitive2D is intended + if (nullptr != pFillGraphicAttribute) + maFillGraphicAttribute = *pFillGraphicAttribute; + // initialize AnimationTiming, needed to detect which frame is requested // in get2DDecomposition createAndSetAnimationTiming(); @@ -404,10 +467,29 @@ namespace drawinglayer::primitive2d // prepare buffer space if (mbBufferingAllowed && isValidData()) { - maBufferedPrimitives = Primitive2DContainer(maAnimation.Count()); + maBufferedPrimitives.resize(maAnimation.Count()); } } + AnimatedGraphicPrimitive2D::~AnimatedGraphicPrimitive2D() + { + // Related: tdf#158807 mutex must be locked when disposing a VirtualDevice + // If the following .ppt document is opened in a debug build + // and the document is left open for a minute or two without + // changing any content, this destructor will be called on a + // non-main thread with the mutex unlocked: + // https://bugs.documentfoundation.org/attachment.cgi?id=46801 + // This hits an assert in VirtualDevice::ReleaseGraphics() so + // explicitly lock the mutex and explicitly dispose and clear + // the VirtualDevice instances variables. + const SolarMutexGuard aSolarGuard; + + maVirtualDevice.disposeAndClear(); + maVirtualDeviceMask.disposeAndClear(); + maAnimation.Clear(); + maGraphic.Clear(); + } + bool AnimatedGraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { // do not use 'GroupPrimitive2D::operator==' here, that would compare @@ -444,7 +526,7 @@ namespace drawinglayer::primitive2d if (aRetval.is()) { - rVisitor.append(aRetval); + rVisitor.visit(aRetval); return; } @@ -463,26 +545,72 @@ namespace drawinglayer::primitive2d if (aRetval.is()) { - rVisitor.append(aRetval); + rVisitor.visit(aRetval); return; } // did not work (not buffered and not 1st frame), create from buffer aRetval = createFromBuffer(); - rVisitor.append(aRetval); + rVisitor.visit(aRetval); + } + + basegfx::B2DRange AnimatedGraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const + { + // get object's range + basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + aUnitRange.transform(getTransform()); + + // intersect with visible part + aUnitRange.intersect(rViewInformation.getViewport()); + + return aUnitRange; } } // end of namespace namespace drawinglayer::primitive2d { + Primitive2DReference createFillGraphicPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const drawinglayer::attribute::FillGraphicAttribute& rFillGraphicAttribute, + double fTransparency) + { + if (basegfx::fTools::equal(fTransparency, 1.0)) + { + // completely transparent, done + return nullptr; + } + + const Graphic& rGraphic(rFillGraphicAttribute.getGraphic()); + const GraphicType aType(rGraphic.GetType()); + + if (GraphicType::Bitmap == aType && rGraphic.IsAnimated()) + { + return new AnimatedGraphicPrimitive2D( + rGraphic, + &rFillGraphicAttribute, + rTransform, + fTransparency); + } + + return new FillGraphicPrimitive2D( + rTransform, + rFillGraphicAttribute, + fTransparency); + } + void create2DDecompositionOfGraphic( Primitive2DContainer& rContainer, const Graphic& rGraphic, - const basegfx::B2DHomMatrix& rTransform) + const basegfx::B2DHomMatrix& rTransform, + double fTransparency) { - Primitive2DContainer aRetval; + if (basegfx::fTools::equal(fTransparency, 1.0)) + { + // completely transparent, done + return; + } switch(rGraphic.GetType()) { @@ -490,18 +618,20 @@ namespace drawinglayer::primitive2d { if(rGraphic.IsAnimated()) { - // prepare specialized AnimatedGraphicPrimitive2D - aRetval.resize(1); - aRetval[0] = new AnimatedGraphicPrimitive2D( + // prepare specialized AnimatedGraphicPrimitive2D, now with + // support for alpha + rContainer.append(new AnimatedGraphicPrimitive2D( rGraphic, - rTransform); + nullptr, + rTransform, + fTransparency)); } else if(rGraphic.getVectorGraphicData()) { // embedded Vector Graphic Data fill, create embed transform const basegfx::B2DRange& rSvgRange(rGraphic.getVectorGraphicData()->getRange()); - if(basegfx::fTools::more(rSvgRange.getWidth(), 0.0) && basegfx::fTools::more(rSvgRange.getHeight(), 0.0)) + if(rSvgRange.getWidth() > 0.0 && rSvgRange.getHeight() > 0.0) { // translate back to origin, scale to unit coordinates basegfx::B2DHomMatrix aEmbedVectorGraphic( @@ -517,18 +647,37 @@ namespace drawinglayer::primitive2d aEmbedVectorGraphic = rTransform * aEmbedVectorGraphic; // add Vector Graphic Data primitives embedded - aRetval.resize(1); - aRetval[0] = new TransformPrimitive2D( - aEmbedVectorGraphic, - rGraphic.getVectorGraphicData()->getPrimitive2DSequence()); + rtl::Reference<BasePrimitive2D> aPrimitive( + new TransformPrimitive2D( + aEmbedVectorGraphic, + Primitive2DContainer(rGraphic.getVectorGraphicData()->getPrimitive2DSequence()))); + + // if needed embed to UnifiedTransparencePrimitive2D + if (!basegfx::fTools::equalZero(fTransparency, 0.0)) + aPrimitive = new UnifiedTransparencePrimitive2D( + Primitive2DContainer { aPrimitive }, fTransparency); + + rContainer.append(aPrimitive); } } else { - aRetval.resize(1); - aRetval[0] = new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(rGraphic.GetBitmapEx()), - rTransform); + // dependent of transparency used create the needed bitmap primitive + if (basegfx::fTools::equalZero(fTransparency)) + { + rContainer.append( + new BitmapPrimitive2D( + rGraphic.GetBitmap(), + rTransform)); + } + else + { + rContainer.append( + new BitmapAlphaPrimitive2D( + rGraphic.GetBitmap(), + rTransform, + fTransparency)); + } } break; @@ -539,10 +688,10 @@ namespace drawinglayer::primitive2d // create MetafilePrimitive2D const GDIMetaFile& rMetafile = rGraphic.GetGDIMetaFile(); - aRetval.resize(1); - aRetval[0] = new MetafilePrimitive2D( - rTransform, - rMetafile); + rtl::Reference<BasePrimitive2D> aPrimitive( + new MetafilePrimitive2D( + rTransform, + rMetafile)); // #i100357# find out if clipping is needed for this primitive. Unfortunately, // there exist Metafiles who's content is bigger than the proposed PrefSize set @@ -559,11 +708,17 @@ namespace drawinglayer::primitive2d basegfx::B2DPolygon aMaskPolygon(basegfx::utils::createUnitPolygon()); aMaskPolygon.transform(rTransform); - Primitive2DReference mask = new MaskPrimitive2D( + aPrimitive = new MaskPrimitive2D( basegfx::B2DPolyPolygon(aMaskPolygon), - aRetval); - aRetval[0] = mask; + Primitive2DContainer { aPrimitive }); } + + // if needed embed to UnifiedTransparencePrimitive2D + if (!basegfx::fTools::equalZero(fTransparency, 0.0)) + aPrimitive = new UnifiedTransparencePrimitive2D( + Primitive2DContainer { aPrimitive }, fTransparency); + + rContainer.append(aPrimitive); break; } @@ -573,12 +728,10 @@ namespace drawinglayer::primitive2d break; } } - - rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); } Primitive2DContainer create2DColorModifierEmbeddingsAsNeeded( - const Primitive2DContainer& rChildren, + Primitive2DContainer&& rChildren, GraphicDrawMode aGraphicDrawMode, double fLuminance, double fContrast, @@ -598,7 +751,7 @@ namespace drawinglayer::primitive2d // set child content as retval; that is what will be used as child content in all // embeddings from here - aRetval = rChildren; + aRetval = std::move(rChildren); if(GraphicDrawMode::Watermark == aGraphicDrawMode) { @@ -620,7 +773,7 @@ namespace drawinglayer::primitive2d // convert to grey const Primitive2DReference aPrimitiveGrey( new ModifiedColorPrimitive2D( - aRetval, + std::move(aRetval), std::make_shared<basegfx::BColorModifier_gray>())); aRetval = Primitive2DContainer { aPrimitiveGrey }; @@ -631,7 +784,7 @@ namespace drawinglayer::primitive2d // convert to mono (black/white with threshold 0.5) const Primitive2DReference aPrimitiveBlackAndWhite( new ModifiedColorPrimitive2D( - aRetval, + std::move(aRetval), std::make_shared<basegfx::BColorModifier_black_and_white>(0.5))); aRetval = Primitive2DContainer { aPrimitiveBlackAndWhite }; @@ -657,7 +810,7 @@ namespace drawinglayer::primitive2d { const Primitive2DReference aPrimitiveRGBLuminannceContrast( new ModifiedColorPrimitive2D( - aRetval, + std::move(aRetval), std::make_shared<basegfx::BColorModifier_RGBLuminanceContrast>( fRed, fGreen, @@ -673,7 +826,7 @@ namespace drawinglayer::primitive2d { const Primitive2DReference aPrimitiveGamma( new ModifiedColorPrimitive2D( - aRetval, + std::move(aRetval), std::make_shared<basegfx::BColorModifier_gamma>( fGamma))); @@ -685,7 +838,7 @@ namespace drawinglayer::primitive2d { const Primitive2DReference aPrimitiveInvert( new ModifiedColorPrimitive2D( - aRetval, + std::move(aRetval), std::make_shared<basegfx::BColorModifier_invert>())); aRetval = Primitive2DContainer { aPrimitiveInvert }; diff --git a/drawinglayer/source/primitive2d/gridprimitive2d.cxx b/drawinglayer/source/primitive2d/gridprimitive2d.cxx index e1d0841678e2..dd6cca55fad6 100644 --- a/drawinglayer/source/primitive2d/gridprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/gridprimitive2d.cxx @@ -20,9 +20,11 @@ #include <drawinglayer/primitive2d/gridprimitive2d.hxx> #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <utility> using namespace com::sun::star; @@ -30,10 +32,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void GridPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference GridPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { if(!(!rViewInformation.getViewport().isEmpty() && getWidth() > 0.0 && getHeight() > 0.0)) - return; + return nullptr; // decompose grid matrix to get logic size basegfx::B2DVector aScale, aTranslate; @@ -158,7 +160,7 @@ namespace drawinglayer::primitive2d } if(aExtendedViewport.isEmpty()) - return; + return nullptr; // prepare point vectors for point and cross markers std::vector< basegfx::B2DPoint > aPositionsPoint; @@ -228,29 +230,31 @@ namespace drawinglayer::primitive2d const sal_uInt32 nCountCross(aPositionsCross.size()); // add PointArrayPrimitive2D if point markers were added + Primitive2DContainer aContainer; if(nCountPoint) { - rContainer.push_back(new PointArrayPrimitive2D(aPositionsPoint, getBColor())); + aContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsPoint), getBColor())); } // add MarkerArrayPrimitive2D if cross markers were added if(!nCountCross) - return; + return new GroupPrimitive2D(std::move(aContainer)); if(!getSubdivisionsX() && !getSubdivisionsY()) { // no subdivisions, so fall back to points at grid positions, no need to // visualize a difference between divisions and sub-divisions - rContainer.push_back(new PointArrayPrimitive2D(aPositionsCross, getBColor())); + aContainer.push_back(new PointArrayPrimitive2D(std::move(aPositionsCross), getBColor())); } else { - rContainer.push_back(new MarkerArrayPrimitive2D(aPositionsCross, getCrossMarker())); + aContainer.push_back(new MarkerArrayPrimitive2D(std::move(aPositionsCross), getCrossMarker())); } + return new GroupPrimitive2D(std::move(aContainer)); } GridPrimitive2D::GridPrimitive2D( - const basegfx::B2DHomMatrix& rTransform, + basegfx::B2DHomMatrix aTransform, double fWidth, double fHeight, double fSmallestViewDistance, @@ -258,9 +262,8 @@ namespace drawinglayer::primitive2d sal_uInt32 nSubdivisionsX, sal_uInt32 nSubdivisionsY, const basegfx::BColor& rBColor, - const BitmapEx& rCrossMarker) - : BufferedDecompositionPrimitive2D(), - maTransform(rTransform), + const Bitmap& rCrossMarker) + : maTransform(std::move(aTransform)), mfWidth(fWidth), mfHeight(fHeight), mfSmallestViewDistance(fSmallestViewDistance), @@ -268,9 +271,7 @@ namespace drawinglayer::primitive2d mnSubdivisionsX(nSubdivisionsX), mnSubdivisionsY(nSubdivisionsY), maBColor(rBColor), - maCrossMarker(rCrossMarker), - maLastObjectToViewTransformation(), - maLastViewport() + maCrossMarker(rCrossMarker) { } @@ -308,18 +309,16 @@ namespace drawinglayer::primitive2d void GridPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - - if(!getBuffered2DDecomposition().empty()) + if(hasBuffered2DDecomposition()) { if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation()) { // conditions of last local decomposition have changed, delete - const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< GridPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember ViewRange and ViewTransformation const_cast< GridPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation(); @@ -331,7 +330,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(GridPrimitive2D, PRIMITIVE2D_ID_GRIDPRIMITIVE2D) + sal_uInt32 GridPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_GRIDPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/groupprimitive2d.cxx b/drawinglayer/source/primitive2d/groupprimitive2d.cxx index 63f21f842b2b..e6428c09e8cd 100644 --- a/drawinglayer/source/primitive2d/groupprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/groupprimitive2d.cxx @@ -26,13 +26,12 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - GroupPrimitive2D::GroupPrimitive2D( const Primitive2DContainer& rChildren ) - : BasePrimitive2D(), - maChildren(rChildren) + GroupPrimitive2D::GroupPrimitive2D( Primitive2DContainer&& aChildren ) + : maChildren(std::move(aChildren)) { } - /** The compare opertator uses the Sequence::==operator, so only checking if + /** The compare operator uses the Sequence::==operator, so only checking if the references are equal. All non-equal references are interpreted as non-equal. */ @@ -54,22 +53,22 @@ namespace drawinglayer::primitive2d getChildren(rVisitor); } - sal_Int64 SAL_CALL GroupPrimitive2D::estimateUsage() + sal_Int64 GroupPrimitive2D::estimateUsage() { size_t nRet(0); for (auto& it : getChildren()) { - uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY); - if (xAcc.is()) - { - nRet += xAcc->estimateUsage(); - } + if (it) + nRet += it->estimateUsage(); } return nRet; } // provide unique ID - ImplPrimitive2DIDBlock(GroupPrimitive2D, PRIMITIVE2D_ID_GROUPPRIMITIVE2D) + sal_uInt32 GroupPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_GROUPPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx index 86c3b88ca6d5..e09c4db606d0 100644 --- a/drawinglayer/source/primitive2d/helplineprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/helplineprimitive2d.cxx @@ -19,10 +19,11 @@ #include <drawinglayer/primitive2d/helplineprimitive2d.hxx> #include <basegfx/polygon/b2dpolygon.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <basegfx/polygon/b2dpolygonclipper.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> using namespace com::sun::star; @@ -30,14 +31,14 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void HelplinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference HelplinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { if(rViewInformation.getViewport().isEmpty() || getDirection().equalZero()) - return; + return nullptr; // position to view coordinates, DashLen and DashLen in logic const basegfx::B2DPoint aViewPosition(rViewInformation.getObjectToViewTransformation() * getPosition()); - + Primitive2DReference xRet; switch(getStyle()) { default : // HelplineStyle2D::Point @@ -52,8 +53,7 @@ namespace drawinglayer::primitive2d aLineA.append(aStartA); aLineA.append(aEndA); aLineA.transform(rViewInformation.getInverseObjectToViewTransformation()); - PolygonMarkerPrimitive2D* pNewA = new PolygonMarkerPrimitive2D(aLineA, getRGBColA(), getRGBColB(), getDiscreteDashLength()); - rContainer.push_back(pNewA); + auto xMarker1 = new PolygonMarkerPrimitive2D(std::move(aLineA), getRGBColA(), getRGBColB(), getDiscreteDashLength()); const basegfx::B2DVector aPerpendicularNormalizedDirection(basegfx::getPerpendicular(aNormalizedDirection)); const basegfx::B2DPoint aStartB(aViewPosition - aPerpendicularNormalizedDirection); @@ -62,9 +62,9 @@ namespace drawinglayer::primitive2d aLineB.append(aStartB); aLineB.append(aEndB); aLineB.transform(rViewInformation.getInverseObjectToViewTransformation()); - PolygonMarkerPrimitive2D* pNewB = new PolygonMarkerPrimitive2D(aLineB, getRGBColA(), getRGBColB(), getDiscreteDashLength()); - rContainer.push_back(pNewB); + auto xMarker2 = new PolygonMarkerPrimitive2D(std::move(aLineB), getRGBColA(), getRGBColB(), getDiscreteDashLength()); + xRet = new GroupPrimitive2D(Primitive2DContainer{xMarker1, xMarker2}); break; } case HelplineStyle2D::Line : @@ -108,19 +108,20 @@ namespace drawinglayer::primitive2d { // clip against visible area const basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolygonOnRange(aLine, rViewInformation.getDiscreteViewport(), true, true)); - + Primitive2DContainer aContainer; for(sal_uInt32 a(0); a < aResult.count(); a++) { basegfx::B2DPolygon aPart(aResult.getB2DPolygon(a)); aPart.transform(rViewInformation.getInverseObjectToViewTransformation()); - PolygonMarkerPrimitive2D* pNew = new PolygonMarkerPrimitive2D(aPart, getRGBColA(), getRGBColB(), getDiscreteDashLength()); - rContainer.push_back(pNew); + aContainer.push_back(new PolygonMarkerPrimitive2D(std::move(aPart), getRGBColA(), getRGBColB(), getDiscreteDashLength())); } + xRet = new GroupPrimitive2D(std::move(aContainer)); } break; } } + return xRet; } HelplinePrimitive2D::HelplinePrimitive2D( @@ -130,15 +131,12 @@ namespace drawinglayer::primitive2d const basegfx::BColor& rRGBColA, const basegfx::BColor& rRGBColB, double fDiscreteDashLength) - : BufferedDecompositionPrimitive2D(), - maPosition(rPosition), + : maPosition(rPosition), maDirection(rDirection), meStyle(eStyle), maRGBColA(rRGBColA), maRGBColB(rRGBColB), - mfDiscreteDashLength(fDiscreteDashLength), - maLastObjectToViewTransformation(), - maLastViewport() + mfDiscreteDashLength(fDiscreteDashLength) { } @@ -161,18 +159,16 @@ namespace drawinglayer::primitive2d void HelplinePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - - if(!getBuffered2DDecomposition().empty()) + if(hasBuffered2DDecomposition()) { if(maLastViewport != rViewInformation.getViewport() || maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation()) { // conditions of last local decomposition have changed, delete - const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< HelplinePrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember ViewRange and ViewTransformation const_cast< HelplinePrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation(); @@ -184,7 +180,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(HelplinePrimitive2D, PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D) + sal_uInt32 HelplinePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx index cdce8a750274..c1a5442b25af 100644 --- a/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/hiddengeometryprimitive2d.cxx @@ -27,8 +27,8 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { HiddenGeometryPrimitive2D::HiddenGeometryPrimitive2D( - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } @@ -42,7 +42,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(HiddenGeometryPrimitive2D, PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D) + sal_uInt32 HiddenGeometryPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/invertprimitive2d.cxx b/drawinglayer/source/primitive2d/invertprimitive2d.cxx index 63c4be5246ac..e2d01381e1a9 100644 --- a/drawinglayer/source/primitive2d/invertprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/invertprimitive2d.cxx @@ -27,13 +27,16 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { InvertPrimitive2D::InvertPrimitive2D( - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } // provide unique ID - ImplPrimitive2DIDBlock(InvertPrimitive2D, PRIMITIVE2D_ID_INVERTPRIMITIVE2D) + sal_uInt32 InvertPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_INVERTPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx index d4b604e79c9f..64364f0df407 100644 --- a/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/markerarrayprimitive2d.cxx @@ -22,6 +22,7 @@ #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <toolkit/helper/vclunohelper.hxx> @@ -30,19 +31,19 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void MarkerArrayPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference MarkerArrayPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { const std::vector< basegfx::B2DPoint >& rPositions = getPositions(); const sal_uInt32 nMarkerCount(rPositions.size()); - if(!(nMarkerCount && !getMarker().IsEmpty())) - return; + if(!nMarkerCount || getMarker().IsEmpty()) + return nullptr; // get pixel size Size aBitmapSize(getMarker().GetSizePixel()); if(!(aBitmapSize.Width() && aBitmapSize.Height())) - return; + return nullptr; // get logic half pixel size basegfx::B2DVector aLogicHalfSize(rViewInformation.getInverseObjectToViewTransformation() * @@ -51,9 +52,9 @@ namespace drawinglayer::primitive2d // use half size for expand aLogicHalfSize *= 0.5; - for(sal_uInt32 a(0); a < nMarkerCount; a++) + Primitive2DContainer aContainer; + for(const auto& rPosition : rPositions) { - const basegfx::B2DPoint& rPosition(rPositions[a]); const basegfx::B2DRange aRange(rPosition - aLogicHalfSize, rPosition + aLogicHalfSize); basegfx::B2DHomMatrix aTransform; @@ -62,18 +63,18 @@ namespace drawinglayer::primitive2d aTransform.set(0, 2, aRange.getMinX()); aTransform.set(1, 2, aRange.getMinY()); - rContainer.push_back( + aContainer.push_back( new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getMarker()), + getMarker(), aTransform)); } + return new GroupPrimitive2D(std::move(aContainer)); } MarkerArrayPrimitive2D::MarkerArrayPrimitive2D( - const std::vector< basegfx::B2DPoint >& rPositions, - const BitmapEx& rMarker) - : BufferedDecompositionPrimitive2D(), - maPositions(rPositions), + std::vector< basegfx::B2DPoint >&& rPositions, + const Bitmap& rMarker) + : maPositions(std::move(rPositions)), maMarker(rMarker) { } @@ -128,7 +129,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(MarkerArrayPrimitive2D, PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D) + sal_uInt32 MarkerArrayPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/maskprimitive2d.cxx b/drawinglayer/source/primitive2d/maskprimitive2d.cxx index 1e8af509c157..630548861a90 100644 --- a/drawinglayer/source/primitive2d/maskprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/maskprimitive2d.cxx @@ -19,6 +19,7 @@ #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <utility> using namespace com::sun::star; @@ -27,10 +28,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { MaskPrimitive2D::MaskPrimitive2D( - const basegfx::B2DPolyPolygon& rMask, - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren), - maMask(rMask) + basegfx::B2DPolyPolygon aMask, + Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)), + maMask(std::move(aMask)) { } @@ -52,7 +53,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(MaskPrimitive2D, PRIMITIVE2D_ID_MASKPRIMITIVE2D) + sal_uInt32 MaskPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_MASKPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx index 5158c6822492..eb70c7602c8c 100644 --- a/drawinglayer/source/primitive2d/mediaprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/mediaprimitive2d.cxx @@ -21,6 +21,7 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <utility> #include <vcl/GraphicObject.hxx> #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> @@ -31,7 +32,7 @@ namespace drawinglayer::primitive2d { - void MediaPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference MediaPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { Primitive2DContainer xRetval; xRetval.resize(1); @@ -69,7 +70,7 @@ namespace drawinglayer::primitive2d { // shrunk primitive has no content (zero size in X or Y), nothing to display. Still create // invisible content for HitTest and BoundRect - const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(xRetval)); + const Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(xRetval))); xRetval = Primitive2DContainer { xHiddenLines, }; } @@ -82,26 +83,26 @@ namespace drawinglayer::primitive2d aTransform.translate(aDestRange.getMinX(), aDestRange.getMinY()); // add transform primitive - const Primitive2DReference aScaled(new TransformPrimitive2D(aTransform, xRetval)); - xRetval = Primitive2DContainer { aScaled }; + xRetval = Primitive2DContainer { + new TransformPrimitive2D(aTransform, std::move(xRetval)) // Scaled + }; } } - rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end()); + return new GroupPrimitive2D(std::move(xRetval)); } MediaPrimitive2D::MediaPrimitive2D( - const basegfx::B2DHomMatrix& rTransform, - const OUString& rURL, + basegfx::B2DHomMatrix aTransform, + OUString aURL, const basegfx::BColor& rBackgroundColor, sal_uInt32 nDiscreteBorder, - const Graphic &rSnapshot) - : BufferedDecompositionPrimitive2D(), - maTransform(rTransform), - maURL(rURL), + Graphic aSnapshot) + : maTransform(std::move(aTransform)), + maURL(std::move(aURL)), maBackgroundColor(rBackgroundColor), mnDiscreteBorder(nDiscreteBorder), - maSnapshot(rSnapshot) + maSnapshot(std::move(aSnapshot)) { } @@ -114,7 +115,8 @@ namespace drawinglayer::primitive2d return (getTransform() == rCompare.getTransform() && maURL == rCompare.maURL && getBackgroundColor() == rCompare.getBackgroundColor() - && getDiscreteBorder() == rCompare.getDiscreteBorder()); + && getDiscreteBorder() == rCompare.getDiscreteBorder() + && maSnapshot.IsNone() == rCompare.maSnapshot.IsNone()); } return false; @@ -138,7 +140,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(MediaPrimitive2D, PRIMITIVE2D_ID_MEDIAPRIMITIVE2D) + sal_uInt32 MediaPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_MEDIAPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx index 362ae446fd99..a0a1550d1b66 100644 --- a/drawinglayer/source/primitive2d/metafileprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/metafileprimitive2d.cxx @@ -18,6 +18,7 @@ */ #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <utility> #include <wmfemfhelper.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> @@ -30,68 +31,67 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void MetafilePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { // Interpret the Metafile and get the content. There should be only one target, as in the start condition, // but iterating will be the right thing to do when some push/pop is not closed Primitive2DContainer xRetval(wmfemfhelper::interpretMetafile(getMetaFile(), rViewInformation)); - if(!xRetval.empty()) + if(xRetval.empty()) + return nullptr; + + // get target size + const ::tools::Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize()); + const basegfx::B2DRange aMtfRange(vcl::unotools::b2DRectangleFromRectangle(aMtfTarget)); + + // tdf#113197 get content range and check if we have an overlap with + // defined target range (aMtfRange) + if (!aMtfRange.isEmpty()) { - // get target size - const ::tools::Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize()); - const basegfx::B2DRange aMtfRange(vcl::unotools::b2DRectangleFromRectangle(aMtfTarget)); + const basegfx::B2DRange aContentRange(xRetval.getB2DRange(rViewInformation)); - // tdf#113197 get content range and check if we have an overlap with - // defined target range (aMtfRange) - if (!aMtfRange.isEmpty()) + // also test equal since isInside gives also true for equal + if (!aMtfRange.equal(aContentRange) && !aMtfRange.isInside(aContentRange)) { - const basegfx::B2DRange aContentRange(xRetval.getB2DRange(rViewInformation)); - - // also test equal since isInside gives also true for equal - if (!aMtfRange.equal(aContentRange) && !aMtfRange.isInside(aContentRange)) - { - // contentRange is partly larger than aMtfRange (stuff sticks - // outside), clipping is needed - const drawinglayer::primitive2d::Primitive2DReference xMask( - new drawinglayer::primitive2d::MaskPrimitive2D( - basegfx::B2DPolyPolygon( - basegfx::utils::createPolygonFromRect( - aMtfRange)), - xRetval)); - - xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask }; - } + // contentRange is partly larger than aMtfRange (stuff sticks + // outside), clipping is needed + const drawinglayer::primitive2d::Primitive2DReference xMask( + new drawinglayer::primitive2d::MaskPrimitive2D( + basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect( + aMtfRange)), + std::move(xRetval))); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask }; } + } - // create transformation - basegfx::B2DHomMatrix aAdaptedTransform; + // create transformation + basegfx::B2DHomMatrix aAdaptedTransform; - aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top()); - aAdaptedTransform.scale( - aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0, - aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0); - aAdaptedTransform = getTransform() * aAdaptedTransform; + aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top()); + aAdaptedTransform.scale( + aMtfTarget.getOpenWidth() ? 1.0 / aMtfTarget.getOpenWidth() : 1.0, + aMtfTarget.getOpenHeight() ? 1.0 / aMtfTarget.getOpenHeight() : 1.0); + aAdaptedTransform = getTransform() * aAdaptedTransform; - // embed to target transformation - const Primitive2DReference aEmbeddedTransform( - new TransformPrimitive2D( - aAdaptedTransform, - xRetval)); + // embed to target transformation + const Primitive2DReference aEmbeddedTransform( + new TransformPrimitive2D( + aAdaptedTransform, + std::move(xRetval))); - xRetval = Primitive2DContainer { aEmbeddedTransform }; - } - - rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end()); + return aEmbeddedTransform; } MetafilePrimitive2D::MetafilePrimitive2D( - const basegfx::B2DHomMatrix& rMetaFileTransform, + basegfx::B2DHomMatrix aMetaFileTransform, const GDIMetaFile& rMetaFile) - : BufferedDecompositionPrimitive2D(), - maMetaFileTransform(rMetaFileTransform), + : maMetaFileTransform(std::move(aMetaFileTransform)), maMetaFile(rMetaFile) { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const @@ -126,7 +126,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D) + sal_uInt32 MetafilePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_METAFILEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx index 4c0e8e6ccd18..9786f9164e7f 100644 --- a/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/modifiedcolorprimitive2d.cxx @@ -19,6 +19,7 @@ #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <utility> using namespace com::sun::star; @@ -27,10 +28,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { ModifiedColorPrimitive2D::ModifiedColorPrimitive2D( - const Primitive2DContainer& rChildren, - const basegfx::BColorModifierSharedPtr& rColorModifier) - : GroupPrimitive2D(rChildren), - maColorModifier(rColorModifier) + Primitive2DContainer&& aChildren, + basegfx::BColorModifierSharedPtr xColorModifier) + : GroupPrimitive2D(std::move(aChildren)), + maColorModifier(std::move(xColorModifier)) { } @@ -57,7 +58,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(ModifiedColorPrimitive2D, PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D) + sal_uInt32 ModifiedColorPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx index 32e66c705e62..0c91957766a4 100644 --- a/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/objectinfoprimitive2d.cxx @@ -18,20 +18,21 @@ */ #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <utility> using namespace com::sun::star; namespace drawinglayer::primitive2d { ObjectInfoPrimitive2D::ObjectInfoPrimitive2D( - const Primitive2DContainer& rChildren, - const OUString& rName, - const OUString& rTitle, - const OUString& rDesc) - : GroupPrimitive2D(rChildren), - maName(rName), - maTitle(rTitle), - maDesc(rDesc) + Primitive2DContainer&& aChildren, + OUString aName, + OUString aTitle, + OUString aDesc) + : GroupPrimitive2D(std::move(aChildren)), + maName(std::move(aName)), + maTitle(std::move(aTitle)), + maDesc(std::move(aDesc)) { } @@ -50,7 +51,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(ObjectInfoPrimitive2D, PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D) + sal_uInt32 ObjectInfoPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx index ff52e1c6e7b5..8ffd7735abd8 100644 --- a/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/pagehierarchyprimitive2d.cxx @@ -24,13 +24,16 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - PageHierarchyPrimitive2D::PageHierarchyPrimitive2D(const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + PageHierarchyPrimitive2D::PageHierarchyPrimitive2D(Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } // provide unique ID - ImplPrimitive2DIDBlock(PageHierarchyPrimitive2D, PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D) + sal_uInt32 PageHierarchyPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_PAGEHIERARCHYPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx index b7c67bf8fd0c..a4280ea1aba0 100644 --- a/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/pagepreviewprimitive2d.cxx @@ -24,6 +24,7 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <utility> using namespace com::sun::star; @@ -31,22 +32,22 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - void PagePreviewPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference PagePreviewPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { Primitive2DContainer aContent(getPageContent()); if(!(!aContent.empty() - && basegfx::fTools::more(getContentWidth(), 0.0) - && basegfx::fTools::more(getContentHeight(), 0.0))) - return; + && getContentWidth() > 0.0) + && getContentHeight() > 0.0) + return nullptr; // the decomposed matrix will be needed basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX; getTransform().decompose(aScale, aTranslate, fRotate, fShearX); - if(!(basegfx::fTools::more(aScale.getX(), 0.0) && basegfx::fTools::more(aScale.getY(), 0.0))) - return; + if(!(aScale.getX() > 0.0 && aScale.getY() > 0.0)) + return nullptr; // check if content overlaps with target size and needs to be embedded with a // clipping primitive @@ -58,7 +59,7 @@ namespace drawinglayer::primitive2d const Primitive2DReference xReferenceA( new MaskPrimitive2D( basegfx::B2DPolyPolygon( - basegfx::utils::createPolygonFromRect(aAllowedContentRange)), aContent)); + basegfx::utils::createPolygonFromRect(aAllowedContentRange)), std::move(aContent))); aContent = Primitive2DContainer { xReferenceA }; } @@ -97,19 +98,18 @@ namespace drawinglayer::primitive2d aPageTrans = aCombined * aPageTrans; // embed in necessary transformation to map from SdrPage to SdrPageObject - rContainer.push_back(new TransformPrimitive2D(aPageTrans, aContent)); + return new TransformPrimitive2D(aPageTrans, std::move(aContent)); } PagePreviewPrimitive2D::PagePreviewPrimitive2D( - const css::uno::Reference< css::drawing::XDrawPage >& rxDrawPage, - const basegfx::B2DHomMatrix& rTransform, + css::uno::Reference< css::drawing::XDrawPage > xDrawPage, + basegfx::B2DHomMatrix aTransform, double fContentWidth, double fContentHeight, - const Primitive2DContainer& rPageContent) - : BufferedDecompositionPrimitive2D(), - mxDrawPage(rxDrawPage), - maPageContent(rPageContent), - maTransform(rTransform), + Primitive2DContainer&& rPageContent) + : mxDrawPage(std::move(xDrawPage)), + maPageContent(std::move(rPageContent)), + maTransform(std::move(aTransform)), mfContentWidth(fContentWidth), mfContentHeight(fContentHeight) { @@ -141,7 +141,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(PagePreviewPrimitive2D, PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D) + sal_uInt32 PagePreviewPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx index ec1f9621452a..cf0b6ff55d4d 100644 --- a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx @@ -28,7 +28,8 @@ #include <drawinglayer/geometry/viewinformation2d.hxx> #include <toolkit/helper/vclunohelper.hxx> -#include <converters.hxx> +#include <drawinglayer/converters.hxx> +#include <utility> using namespace com::sun::star; @@ -93,6 +94,30 @@ namespace drawinglayer::primitive2d } } + void PatternFillPrimitive2D::getTileSize( + sal_uInt32& rWidth, + sal_uInt32& rHeight, + const geometry::ViewInformation2D& rViewInformation) const + { + const basegfx::B2DRange aMaskRange(getMask().getB2DRange()); + + // get discrete rounded up square size of a single tile + const basegfx::B2DHomMatrix aMaskRangeTransformation( + basegfx::utils::createScaleTranslateB2DHomMatrix( + aMaskRange.getRange(), + aMaskRange.getMinimum())); + const basegfx::B2DHomMatrix aTransform( + rViewInformation.getObjectToViewTransformation() * aMaskRangeTransformation); + const basegfx::B2DPoint aTopLeft(aTransform * getReferenceRange().getMinimum()); + const basegfx::B2DPoint aX(aTransform * basegfx::B2DPoint(getReferenceRange().getMaxX(), getReferenceRange().getMinY())); + const basegfx::B2DPoint aY(aTransform * basegfx::B2DPoint(getReferenceRange().getMinX(), getReferenceRange().getMaxY())); + const double fW(basegfx::B2DVector(aX - aTopLeft).getLength()); + const double fH(basegfx::B2DVector(aY - aTopLeft).getLength()); + + rWidth = basegfx::fround(ceil(fW)); + rHeight = basegfx::fround(ceil(fH)); + } + Primitive2DContainer PatternFillPrimitive2D::createContent(const geometry::ViewInformation2D& rViewInformation) const { Primitive2DContainer aContent; @@ -101,29 +126,29 @@ namespace drawinglayer::primitive2d if(0 != mnDiscreteWidth && 0 != mnDiscreteHeight) { const geometry::ViewInformation2D aViewInformation2D; - const primitive2d::Primitive2DReference xEmbedRef( - new primitive2d::TransformPrimitive2D( - basegfx::utils::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight), - getChildren())); - const primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef }; - - const BitmapEx aBitmapEx( - convertToBitmapEx( - xEmbedSeq, + primitive2d::Primitive2DContainer xEmbedSeq { + new primitive2d::TransformPrimitive2D( + basegfx::utils::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight), + Primitive2DContainer(getChildren())) + }; + + const Bitmap aBitmap( + convertToBitmap( + std::move(xEmbedSeq), aViewInformation2D, mnDiscreteWidth, mnDiscreteHeight, mnDiscreteWidth * mnDiscreteHeight)); - if(!aBitmapEx.IsEmpty()) + if(!aBitmap.IsEmpty()) { - const Size& rBmpPix = aBitmapEx.GetSizePixel(); + const Size aBmpPix = aBitmap.GetSizePixel(); - if(rBmpPix.Width() > 0 && rBmpPix.Height() > 0) + if(aBmpPix.Width() > 0 && aBmpPix.Height() > 0) { const primitive2d::Primitive2DReference xEmbedRefBitmap( new primitive2d::BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(aBitmapEx), + aBitmap, basegfx::B2DHomMatrix())); aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap }; } @@ -142,14 +167,14 @@ namespace drawinglayer::primitive2d // check if content needs to be clipped const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); - const basegfx::B2DRange aContentRange(getChildren().getB2DRange(rViewInformation)); + const basegfx::B2DRange aContentRange(aContent.getB2DRange(rViewInformation)); if(!aUnitRange.isInside(aContentRange)) { const Primitive2DReference xRef( new MaskPrimitive2D( basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aUnitRange)), - aContent)); + std::move(aContent))); aContent = Primitive2DContainer { xRef }; } @@ -158,20 +183,39 @@ namespace drawinglayer::primitive2d return aContent; } - void PatternFillPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + // create buffered content in given resolution + Bitmap PatternFillPrimitive2D::createTileImage(sal_uInt32 nWidth, sal_uInt32 nHeight) const + { + const geometry::ViewInformation2D aViewInformation2D; + Primitive2DContainer aContent(createContent(aViewInformation2D)); + const primitive2d::Primitive2DReference xEmbedRef( + new primitive2d::TransformPrimitive2D( + basegfx::utils::createScaleB2DHomMatrix(nWidth, nHeight), + std::move(aContent))); + primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef }; + + return convertToBitmap( + std::move(xEmbedSeq), + aViewInformation2D, + nWidth, + nHeight, + nWidth * nHeight); + } + + Primitive2DReference PatternFillPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { Primitive2DContainer aRetval; if(getChildren().empty()) - return; + return nullptr; if(!(!getReferenceRange().isEmpty() && getReferenceRange().getWidth() > 0.0 && getReferenceRange().getHeight() > 0.0)) - return; + return nullptr; const basegfx::B2DRange aMaskRange(getMask().getB2DRange()); if(!(!aMaskRange.isEmpty() && aMaskRange.getWidth() > 0.0 && aMaskRange.getHeight() > 0.0)) - return; + return nullptr; // create tiling matrices std::vector< basegfx::B2DHomMatrix > aMatrices; @@ -180,7 +224,7 @@ namespace drawinglayer::primitive2d aTiling.appendTransformations(aMatrices); // create content - const Primitive2DContainer aContent(createContent(rViewInformation)); + Primitive2DContainer aContent(createContent(rViewInformation)); // resize result aRetval.resize(aMatrices.size()); @@ -190,7 +234,7 @@ namespace drawinglayer::primitive2d { aRetval[a] = new TransformPrimitive2D( aMatrices[a], - aContent); + Primitive2DContainer(aContent)); } // transform result which is in unit coordinates to mask's object coordinates @@ -200,30 +244,26 @@ namespace drawinglayer::primitive2d aMaskRange.getRange(), aMaskRange.getMinimum())); - const Primitive2DReference xRef( - new TransformPrimitive2D( - aMaskTransform, - aRetval)); - - aRetval = Primitive2DContainer { xRef }; + aRetval = Primitive2DContainer { + new TransformPrimitive2D( + aMaskTransform, + std::move(aRetval)) + }; } // embed result in mask - { - rContainer.push_back( - new MaskPrimitive2D( - getMask(), - aRetval)); - } + return + new MaskPrimitive2D( + getMask(), + std::move(aRetval)); } PatternFillPrimitive2D::PatternFillPrimitive2D( - const basegfx::B2DPolyPolygon& rMask, - const Primitive2DContainer& rChildren, + basegfx::B2DPolyPolygon aMask, + Primitive2DContainer&& rChildren, const basegfx::B2DRange& rReferenceRange) - : BufferedDecompositionPrimitive2D(), - maMask(rMask), - maChildren(rChildren), + : maMask(std::move(aMask)), + maChildren(std::move(rChildren)), maReferenceRange(rReferenceRange), mnDiscreteWidth(0), mnDiscreteHeight(0) @@ -301,29 +341,27 @@ namespace drawinglayer::primitive2d PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this); pThat->mnDiscreteWidth = nW; pThat->mnDiscreteHeight = nH; - pThat->setBuffered2DDecomposition(Primitive2DContainer()); + pThat->setBuffered2DDecomposition(nullptr); } // call parent BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); } - sal_Int64 SAL_CALL PatternFillPrimitive2D::estimateUsage() + sal_Int64 PatternFillPrimitive2D::estimateUsage() { size_t nRet(0); for (auto& it : getChildren()) - { - uno::Reference<util::XAccounting> const xAcc(it, uno::UNO_QUERY); - if (xAcc.is()) - { - nRet += xAcc->estimateUsage(); - } - } + if (it) + nRet += it->estimateUsage(); return nRet; } // provide unique ID - ImplPrimitive2DIDBlock(PatternFillPrimitive2D, PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D) + sal_uInt32 PatternFillPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx index 7e527487eb85..6299e185083c 100644 --- a/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/pointarrayprimitive2d.cxx @@ -27,12 +27,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { PointArrayPrimitive2D::PointArrayPrimitive2D( - const std::vector< basegfx::B2DPoint >& rPositions, + std::vector< basegfx::B2DPoint >&& rPositions, const basegfx::BColor& rRGBColor) - : BasePrimitive2D(), - maPositions(rPositions), - maRGBColor(rRGBColor), - maB2DRange() + : maPositions(std::move(rPositions)), + maRGBColor(rRGBColor) { } @@ -69,7 +67,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(PointArrayPrimitive2D, PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D) + sal_uInt32 PointArrayPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx index 0251910e8252..329911541d4e 100644 --- a/drawinglayer/source/primitive2d/polygonprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/polygonprimitive2d.cxx @@ -17,587 +17,812 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <basegfx/polygon/b2dlinegeometry.hxx> #include <com/sun/star/drawing/LineCap.hpp> - -#include <converters.hxx> +#include <utility> using namespace com::sun::star; -using namespace std; - -namespace drawinglayer::primitive2d +namespace +{ +void implGrowHairline(basegfx::B2DRange& rRange, + const drawinglayer::geometry::ViewInformation2D& rViewInformation) { - PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const basegfx::BColor& rBColor) - : BasePrimitive2D(), - maPolygon(rPolygon), - maBColor(rBColor) + if (!rRange.isEmpty()) + { + // Calculate view-dependent hairline width + const basegfx::B2DVector aDiscreteSize( + rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); + const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); + + if (fDiscreteHalfLineWidth > 0.0) { + rRange.grow(fDiscreteHalfLineWidth); } + } +} +} - bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BasePrimitive2D::operator==(rPrimitive)) - { - const PolygonHairlinePrimitive2D& rCompare = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive); +namespace drawinglayer::primitive2d +{ +PolygonHairlinePrimitive2D::PolygonHairlinePrimitive2D(basegfx::B2DPolygon aPolygon, + const basegfx::BColor& rBColor) + : maPolygon(std::move(aPolygon)) + , maBColor(rBColor) +{ +} - return (getB2DPolygon() == rCompare.getB2DPolygon() - && getBColor() == rCompare.getBColor()); - } +bool PolygonHairlinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const PolygonHairlinePrimitive2D& rCompare + = static_cast<const PolygonHairlinePrimitive2D&>(rPrimitive); - return false; - } + return (getB2DPolygon() == rCompare.getB2DPolygon() && getBColor() == rCompare.getBColor()); + } - basegfx::B2DRange PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const - { - // this is a hairline, thus the line width is view-dependent. Get range of polygon - // as base size - basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange()); + return false; +} - if(!aRetval.isEmpty()) - { - // Calculate view-dependent hairline width - const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); - const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); - - if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0)) - { - aRetval.grow(fDiscreteHalfLineWidth); - } - } +basegfx::B2DRange +PolygonHairlinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // this is a hairline, thus the line width is view-dependent. Get range of polygon + // as base size + basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange()); - // return range - return aRetval; - } + // Calculate and grow by view-dependent hairline width + implGrowHairline(aRetval, rViewInformation); - // provide unique ID - ImplPrimitive2DIDBlock(PolygonHairlinePrimitive2D, PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D) + // return range + return aRetval; +} +// provide unique ID +sal_uInt32 PolygonHairlinePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D; +} + +SingleLinePrimitive2D::SingleLinePrimitive2D(const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + const basegfx::BColor& rBColor) + : BasePrimitive2D() + , maStart(rStart) + , maEnd(rEnd) + , maBColor(rBColor) +{ +} - void PolygonMarkerPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const - { - // calculate logic DashLength - const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(getDiscreteDashLength(), 0.0)); - const double fLogicDashLength(aDashVector.getX()); +bool SingleLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const SingleLinePrimitive2D& rCompare( + static_cast<const SingleLinePrimitive2D&>(rPrimitive)); - if(fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB())) - { - // apply dashing; get line and gap snippets - std::vector< double > aDash; - basegfx::B2DPolyPolygon aDashedPolyPolyA; - basegfx::B2DPolyPolygon aDashedPolyPolyB; + return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd() + && getBColor() == rCompare.getBColor()); + } - aDash.push_back(fLogicDashLength); - aDash.push_back(fLogicDashLength); - basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA, &aDashedPolyPolyB, 2.0 * fLogicDashLength); + return false; +} - rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyA, getRGBColorA())); - rContainer.push_back(new PolyPolygonHairlinePrimitive2D(aDashedPolyPolyB, getRGBColorB())); - } - else - { - rContainer.push_back(new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA())); - } - } +basegfx::B2DRange +SingleLinePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DRange aRetval(getStart(), getEnd()); - PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const basegfx::BColor& rRGBColorA, - const basegfx::BColor& rRGBColorB, - double fDiscreteDashLength) - : BufferedDecompositionPrimitive2D(), - maPolygon(rPolygon), - maRGBColorA(rRGBColorA), - maRGBColorB(rRGBColorB), - mfDiscreteDashLength(fDiscreteDashLength), - maLastInverseObjectToViewTransformation() - { - } + // Calculate and grow by view-dependent hairline width + implGrowHairline(aRetval, rViewInformation); - bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const PolygonMarkerPrimitive2D& rCompare = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive); + return aRetval; +} - return (getB2DPolygon() == rCompare.getB2DPolygon() - && getRGBColorA() == rCompare.getRGBColorA() - && getRGBColorB() == rCompare.getRGBColorB() - && getDiscreteDashLength() == rCompare.getDiscreteDashLength()); - } +sal_uInt32 SingleLinePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D; +} - return false; - } +void SingleLinePrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (getStart() == getEnd()) + { + // single point + Primitive2DContainer aSequence = { new PointArrayPrimitive2D( + std::vector<basegfx::B2DPoint>{ getStart() }, getBColor()) }; + rVisitor.visit(aSequence); + } + else + { + // line + Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D( + basegfx::B2DPolygon{ getStart(), getEnd() }, getBColor()) }; + rVisitor.visit(aSequence); + } +} - basegfx::B2DRange PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const - { - // this is a hairline, thus the line width is view-dependent. Get range of polygon - // as base size - basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange()); +LineRectanglePrimitive2D::LineRectanglePrimitive2D(const basegfx::B2DRange& rB2DRange, + const basegfx::BColor& rBColor) + : BasePrimitive2D() + , maB2DRange(rB2DRange) + , maBColor(rBColor) +{ +} - if(!aRetval.isEmpty()) - { - // Calculate view-dependent hairline width - const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); - const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); - - if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0)) - { - aRetval.grow(fDiscreteHalfLineWidth); - } - } +bool LineRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const LineRectanglePrimitive2D& rCompare( + static_cast<const LineRectanglePrimitive2D&>(rPrimitive)); - // return range - return aRetval; - } + return (getB2DRange() == rCompare.getB2DRange() && getBColor() == rCompare.getBColor()); + } - void PolygonMarkerPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const - { - ::osl::MutexGuard aGuard( m_aMutex ); - bool bNeedNewDecomposition(false); + return false; +} - if(!getBuffered2DDecomposition().empty()) - { - if(rViewInformation.getInverseObjectToViewTransformation() != maLastInverseObjectToViewTransformation) - { - bNeedNewDecomposition = true; - } - } +basegfx::B2DRange +LineRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DRange aRetval(getB2DRange()); - if(bNeedNewDecomposition) - { - // conditions of last local decomposition have changed, delete - const_cast< PolygonMarkerPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); - } + // Calculate and grow by view-dependent hairline width + implGrowHairline(aRetval, rViewInformation); - if(getBuffered2DDecomposition().empty()) - { - // remember last used InverseObjectToViewTransformation - PolygonMarkerPrimitive2D* pThat = const_cast< PolygonMarkerPrimitive2D* >(this); - pThat->maLastInverseObjectToViewTransformation = rViewInformation.getInverseObjectToViewTransformation(); - } + return aRetval; +} - // use parent implementation - BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); - } +sal_uInt32 LineRectanglePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D; +} - // provide unique ID - ImplPrimitive2DIDBlock(PolygonMarkerPrimitive2D, PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D) +void LineRectanglePrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (getB2DRange().isEmpty()) + { + // no geometry, done + return; + } -} // end of namespace + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(getB2DRange())); + Primitive2DContainer aSequence = { new PolygonHairlinePrimitive2D(aPolygon, getBColor()) }; + rVisitor.visit(aSequence); +} -namespace drawinglayer +Primitive2DReference PolygonMarkerPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& rViewInformation) const { - namespace primitive2d + // calculate logic DashLength + const basegfx::B2DVector aDashVector(rViewInformation.getInverseObjectToViewTransformation() + * basegfx::B2DVector(getDiscreteDashLength(), 0.0)); + const double fLogicDashLength(aDashVector.getX()); + + if (fLogicDashLength > 0.0 && !getRGBColorA().equal(getRGBColorB())) { - void PolygonStrokePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const - { - if(!getB2DPolygon().count()) - return; + // apply dashing; get line and gap snippets + std::vector<double> aDash; + basegfx::B2DPolyPolygon aDashedPolyPolyA; + basegfx::B2DPolyPolygon aDashedPolyPolyB; + + aDash.push_back(fLogicDashLength); + aDash.push_back(fLogicDashLength); + basegfx::utils::applyLineDashing(getB2DPolygon(), aDash, &aDashedPolyPolyA, + &aDashedPolyPolyB, 2.0 * fLogicDashLength); + + Primitive2DContainer aContainer; + aContainer.push_back( + new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyA), getRGBColorA())); + aContainer.push_back( + new PolyPolygonHairlinePrimitive2D(std::move(aDashedPolyPolyB), getRGBColorB())); + return new GroupPrimitive2D(std::move(aContainer)); + } + else + { + return new PolygonHairlinePrimitive2D(getB2DPolygon(), getRGBColorA()); + } +} + +PolygonMarkerPrimitive2D::PolygonMarkerPrimitive2D(basegfx::B2DPolygon aPolygon, + const basegfx::BColor& rRGBColorA, + const basegfx::BColor& rRGBColorB, + double fDiscreteDashLength) + : maPolygon(std::move(aPolygon)) + , maRGBColorA(rRGBColorA) + , maRGBColorB(rRGBColorB) + , mfDiscreteDashLength(fDiscreteDashLength) +{ +} + +bool PolygonMarkerPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const PolygonMarkerPrimitive2D& rCompare + = static_cast<const PolygonMarkerPrimitive2D&>(rPrimitive); - // #i102241# try to simplify before usage - const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon())); - basegfx::B2DPolyPolygon aHairLinePolyPolygon; + return (getB2DPolygon() == rCompare.getB2DPolygon() + && getRGBColorA() == rCompare.getRGBColorA() + && getRGBColorB() == rCompare.getRGBColorB() + && getDiscreteDashLength() == rCompare.getDiscreteDashLength()); + } - if(getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen()) - { - // no line dashing, just copy - aHairLinePolyPolygon.append(aB2DPolygon); - } - else - { - // apply LineStyle - basegfx::utils::applyLineDashing( - aB2DPolygon, getStrokeAttribute().getDotDashArray(), - &aHairLinePolyPolygon, nullptr, getStrokeAttribute().getFullDotDashLen()); - } + return false; +} - const sal_uInt32 nCount(aHairLinePolyPolygon.count()); +basegfx::B2DRange +PolygonMarkerPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // this is a hairline, thus the line width is view-dependent. Get range of polygon + // as base size + basegfx::B2DRange aRetval(getB2DPolygon().getB2DRange()); - if(!getLineAttribute().isDefault() && getLineAttribute().getWidth()) - { - // create fat line data - const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0); - const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin()); - const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap()); - basegfx::B2DPolyPolygon aAreaPolyPolygon; - const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle()); - - for(sal_uInt32 a(0); a < nCount; a++) - { - // New version of createAreaGeometry; now creates bezier polygons - aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry( - aHairLinePolyPolygon.getB2DPolygon(a), - fHalfLineWidth, - aLineJoin, - aLineCap, - basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ , - 0.4 /* default fMaxPartOfEdge*/ , - fMiterMinimumAngle)); - } - - // create primitive - for(sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++) - { - // put into single polyPolygon primitives to make clear that this is NOT meant - // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a - // melting process may be used here one day. - const basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b)); - const basegfx::BColor aColor(getLineAttribute().getColor()); - rContainer.push_back(new PolyPolygonColorPrimitive2D(aNewPolyPolygon, aColor)); - } - } - else - { - rContainer.push_back( - new PolyPolygonHairlinePrimitive2D( - aHairLinePolyPolygon, - getLineAttribute().getColor())); - } - } + if (!aRetval.isEmpty()) + { + // Calculate view-dependent hairline width + const basegfx::B2DVector aDiscreteSize( + rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); + const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); - PolygonStrokePrimitive2D::PolygonStrokePrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute, - const attribute::StrokeAttribute& rStrokeAttribute) - : BufferedDecompositionPrimitive2D(), - maPolygon(rPolygon), - maLineAttribute(rLineAttribute), - maStrokeAttribute(rStrokeAttribute) + if (fDiscreteHalfLineWidth > 0.0) { - // MM01: keep these - these are no curve-decompposers but just checks - // simplify curve segments: moved here to not need to use it - // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect - maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon); + aRetval.grow(fDiscreteHalfLineWidth); } + } + + // return range + return aRetval; +} + +void PolygonMarkerPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + bool bNeedNewDecomposition(false); - PolygonStrokePrimitive2D::PolygonStrokePrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute) - : BufferedDecompositionPrimitive2D(), - maPolygon(rPolygon), - maLineAttribute(rLineAttribute), - maStrokeAttribute() + if (hasBuffered2DDecomposition()) + { + if (rViewInformation.getInverseObjectToViewTransformation() + != maLastInverseObjectToViewTransformation) { - // MM01: keep these - these are no curve-decompposers but just checks - // simplify curve segments: moved here to not need to use it - // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect - maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon); + bNeedNewDecomposition = true; } + } - bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) - { - const PolygonStrokePrimitive2D& rCompare = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive); + if (bNeedNewDecomposition) + { + // conditions of last local decomposition have changed, delete + const_cast<PolygonMarkerPrimitive2D*>(this)->setBuffered2DDecomposition(nullptr); + } - return (getB2DPolygon() == rCompare.getB2DPolygon() - && getLineAttribute() == rCompare.getLineAttribute() - && getStrokeAttribute() == rCompare.getStrokeAttribute()); - } + if (!hasBuffered2DDecomposition()) + { + // remember last used InverseObjectToViewTransformation + PolygonMarkerPrimitive2D* pThat = const_cast<PolygonMarkerPrimitive2D*>(this); + pThat->maLastInverseObjectToViewTransformation + = rViewInformation.getInverseObjectToViewTransformation(); + } + + // use parent implementation + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + +// provide unique ID +sal_uInt32 PolygonMarkerPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D; +} + +} // end of namespace + +namespace drawinglayer::primitive2d +{ +Primitive2DReference PolygonStrokePrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (!getB2DPolygon().count()) + return nullptr; + + // #i102241# try to simplify before usage + const basegfx::B2DPolygon aB2DPolygon(basegfx::utils::simplifyCurveSegments(getB2DPolygon())); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; + + if (getStrokeAttribute().isDefault() || 0.0 == getStrokeAttribute().getFullDotDashLen()) + { + // no line dashing, just copy + aHairLinePolyPolygon.append(aB2DPolygon); + } + else + { + // apply LineStyle + basegfx::utils::applyLineDashing(aB2DPolygon, getStrokeAttribute().getDotDashArray(), + &aHairLinePolyPolygon, nullptr, + getStrokeAttribute().getFullDotDashLen()); + } + + const sal_uInt32 nCount(aHairLinePolyPolygon.count()); - return false; + if (!getLineAttribute().isDefault() && getLineAttribute().getWidth()) + { + // create fat line data + const double fHalfLineWidth(getLineAttribute().getWidth() / 2.0); + const basegfx::B2DLineJoin aLineJoin(getLineAttribute().getLineJoin()); + const css::drawing::LineCap aLineCap(getLineAttribute().getLineCap()); + basegfx::B2DPolyPolygon aAreaPolyPolygon; + const double fMiterMinimumAngle(getLineAttribute().getMiterMinimumAngle()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + // New version of createAreaGeometry; now creates bezier polygons + aAreaPolyPolygon.append(basegfx::utils::createAreaGeometry( + aHairLinePolyPolygon.getB2DPolygon(a), fHalfLineWidth, aLineJoin, aLineCap, + basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/, + 0.4 /* default fMaxPartOfEdge*/, fMiterMinimumAngle)); } - basegfx::B2DRange PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const + // create primitive + Primitive2DContainer aContainer; + for (sal_uInt32 b(0); b < aAreaPolyPolygon.count(); b++) { - basegfx::B2DRange aRetval; + // put into single polyPolygon primitives to make clear that this is NOT meant + // to be painted as a single tools::PolyPolygon (XORed as fill rule). Alternatively, a + // melting process may be used here one day. + basegfx::B2DPolyPolygon aNewPolyPolygon(aAreaPolyPolygon.getB2DPolygon(b)); + const basegfx::BColor aColor(getLineAttribute().getColor()); + aContainer.push_back( + new PolyPolygonColorPrimitive2D(std::move(aNewPolyPolygon), aColor)); + } + return new GroupPrimitive2D(std::move(aContainer)); + } + else + { + return new PolyPolygonHairlinePrimitive2D(std::move(aHairLinePolyPolygon), + getLineAttribute().getColor()); + } +} + +PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon, + const attribute::LineAttribute& rLineAttribute, + attribute::StrokeAttribute aStrokeAttribute) + : maPolygon(std::move(aPolygon)) + , maLineAttribute(rLineAttribute) + , maStrokeAttribute(std::move(aStrokeAttribute)) + , maBufferedRange() +{ + // MM01: keep these - these are no curve-decompposers but just checks + // simplify curve segments: moved here to not need to use it + // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect + maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon); +} + +PolygonStrokePrimitive2D::PolygonStrokePrimitive2D(basegfx::B2DPolygon aPolygon, + const attribute::LineAttribute& rLineAttribute) + : maPolygon(std::move(aPolygon)) + , maLineAttribute(rLineAttribute) + , maBufferedRange() +{ + // MM01: keep these - these are no curve-decompposers but just checks + // simplify curve segments: moved here to not need to use it + // at VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect + maPolygon = basegfx::utils::simplifyCurveSegments(maPolygon); +} - if(getLineAttribute().getWidth()) - { - bool bUseDecomposition(false); - - if(basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin()) - { - // if line is mitered, use parent call since mitered line - // geometry may use more space than the geometry grown by half line width - bUseDecomposition = true; - } - - if(!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap()) - { - // when drawing::LineCap_SQUARE is used the below method to grow the polygon - // range by half line width will not work, so use decomposition. Interestingly, - // the grow method below works perfectly for LineCap_ROUND since the grow is in - // all directions and the rounded cap needs the same grow in all directions independent - // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE - bUseDecomposition = true; - } - - if (bUseDecomposition) - { - // get correct range by using the decomposition fallback, reasons see above cases - - // ofz#947 to optimize calculating the range, turn any slow dashes into a solid line - // when just calculating bounds - attribute::StrokeAttribute aOrigStrokeAttribute = maStrokeAttribute; - const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = attribute::StrokeAttribute(); - aRetval = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); - const_cast<PolygonStrokePrimitive2D*>(this)->maStrokeAttribute = aOrigStrokeAttribute; - } - else - { - // for all other B2DLINEJOIN_* get the range from the base geometry - // and expand by half the line width - aRetval = getB2DPolygon().getB2DRange(); - aRetval.grow(getLineAttribute().getWidth() * 0.5); - } - } - else - { - // this is a hairline, thus the line width is view-dependent. Get range of polygon - // as base size - aRetval = getB2DPolygon().getB2DRange(); - - if(!aRetval.isEmpty()) - { - // Calculate view-dependent hairline width - const basegfx::B2DVector aDiscreteSize(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); - const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); - - if(basegfx::fTools::more(fDiscreteHalfLineWidth, 0.0)) - { - aRetval.grow(fDiscreteHalfLineWidth); - } - } - } +bool PolygonStrokePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const PolygonStrokePrimitive2D& rCompare + = static_cast<const PolygonStrokePrimitive2D&>(rPrimitive); - return aRetval; - } + return (getB2DPolygon() == rCompare.getB2DPolygon() + && getLineAttribute() == rCompare.getLineAttribute() + && getStrokeAttribute() == rCompare.getStrokeAttribute()); + } - // provide unique ID - ImplPrimitive2DIDBlock(PolygonStrokePrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D) + return false; +} +basegfx::B2DRange +PolygonStrokePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + if (!maBufferedRange.isEmpty()) + { + // use the view-independent, buffered B2DRange + return maBufferedRange; + } + if (getLineAttribute().getWidth()) + { + bool bUseDecomposition(false); - void PolygonWavePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + if (basegfx::B2DLineJoin::Miter == getLineAttribute().getLineJoin()) { - if(!getB2DPolygon().count()) - return; + // if line is mitered, use parent call since mitered line + // geometry may use more space than the geometry grown by half line width + bUseDecomposition = true; + } - const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth())); - const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight())); + if (!bUseDecomposition && css::drawing::LineCap_SQUARE == getLineAttribute().getLineCap()) + { + // when drawing::LineCap_SQUARE is used the below method to grow the polygon + // range by half line width will not work, so use decomposition. Interestingly, + // the grow method below works perfectly for LineCap_ROUND since the grow is in + // all directions and the rounded cap needs the same grow in all directions independent + // from its orientation. Unfortunately this is not the case for drawing::LineCap_SQUARE + + // NOTE: I thought about using [sqrt(2) * 0.5] a a factor for LineCap_SQUARE and not + // set bUseDecomposition. I even tried that it works. Then an auto-test failing showed + // not only that view-dependent stuff needs to be considered (what is done for the + // hairline case below), *BUT* also e.g. conversions to PNG/exports use the B2DRange + // of the geometry, so: + // - expanding by 1/2 LineWidth is OK for rounded + // - expanding by more (like sqrt(2) * 0.5 * LineWidth) immediately extends the size + // of e.g. geometry converted to PNG, plus many similar cases that cannot be thought + // of in advance. + // This means that converting those thought-experiment examples in (4) will and do lead + // to bigger e.g. Bitmap conversion(s), not avoiding but painting the free space. That + // could only be done by correctly and fully decomposing the geometry, including + // stroke, and accepting the cost... + bUseDecomposition = true; + } - if(bHasWidth && bHasHeight) + if (bUseDecomposition) + { + // get correct range by using the decomposition fallback, reasons see above cases + + // It is not a good idea to temporarily (re)set the PolygonStrokePrimitive2D + // to default. While it is understandable to use the speed advantage, it is + // bad for quite some reasons: + // + // (1) As described in include/drawinglayer/primitive2d/baseprimitive2d.hxx + // a Primitive is "noncopyable to make clear that a primitive is a read-only + // instance and copying or changing values is not intended". This is the base + // assumption for many decisions for Primitive handling. + // (2) For example, that the decomposition is *always* re-usable. It cannot change + // and is correct when it already exists since the values the decomposition is + // based on cannot change. + // (3) If this *is* done (like it was here) and the Primitive is derived from + // BufferedDecompositionPrimitive2D and thus buffers it's decomposition, + // the risk is that in this case the *wrong* decomposition will be used by + // other PrimitiveProcessors. Maybe not by the VclPixelProcessor2D/VclProcessor2D + // since it handles this primitive directly - not even sure for all cases. + // Sooner or later another PrimitiveProcessor will re-use this wrong temporary + // decomposition, and as an error, a non-stroked line will be painted/used. + // (4) The B2DRange is not strictly defined as minimal bound for the geometry, + // but it should be as small/tight as possible. Making it larger risks more + // area to be invalidated (repaint) and processed (all geometric stuff,l may + // include future and existing exports to other formats which are or will be + // implemented as PrimitiveProcessor). It is easy to imagine cases with much + // too large B2DRange - a line with a pattern that would solve to a single + // small start-rectangle and rest is empty, or a circle with a stroke that + // makes only a quarter of it visible. + // + // The reason to do this is speed, what is a good argument. But speed should + // only be used if the pair of [correctness/speed] does not sacrifice the correctness + // over the speed. + // Luckily there are alternatives to solve this and to keep [correctness/speed] + // valid: + // + // (a) Reset the temporary decomposition after having const-casted and + // changed maStrokeAttribute. + // Disadvantage: More const-cast hacks, plus this temporary decomposition + // will be potentially done repeatedly (every time + // PolygonStrokePrimitive2D::getB2DRange is called) + // (b) Use a temporary, local PolygonStrokePrimitive2D here, with neutral + // PolygonStrokePrimitive2D and call ::getB2DRange() at it. That way + // the buffered decomposition will not be harmed. + // Disadvantage: Same as (a), decomposition will be potentially done repeatedly + // (c) Use a temporary, local PolygonStrokePrimitive2D and buffer B2DRange + // locally for this Primitive. Due to (1)/(2) this cannot change, so + // when calculated once it is totally legal to use it. + // + // Thus here I would use (c): It accepts the disadvantages of (4) over speed, but + // avoids the errors/problems from (1-4). + // Additional argument for this: The hairline case below *also* uses the full + // B2DRange of the polygon, ignoring an evtl. stroke, so (4) applies + if (!getStrokeAttribute().isDefault()) { - // create waveline curve - const basegfx::B2DPolygon aWaveline(basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight())); - rContainer.push_back(new PolygonStrokePrimitive2D(aWaveline, getLineAttribute(), getStrokeAttribute())); + // only do this if StrokeAttribute is used, else recursion may happen (!) + const rtl::Reference<primitive2d::PolygonStrokePrimitive2D> + aTemporaryPrimitiveWithoutStroke(new primitive2d::PolygonStrokePrimitive2D( + getB2DPolygon(), getLineAttribute())); + maBufferedRange + = aTemporaryPrimitiveWithoutStroke + ->BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); } else { - // flat waveline, decompose to simple line primitive - rContainer.push_back(new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(), getStrokeAttribute())); + // fallback to normal decompose, that result can be used for visualization + // later, too. Still buffer B2DRange in maBufferedRange, so it needs to be + // merged into one B2DRange only once + maBufferedRange = BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); } } - - PolygonWavePrimitive2D::PolygonWavePrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute, - const attribute::StrokeAttribute& rStrokeAttribute, - double fWaveWidth, - double fWaveHeight) - : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute), - mfWaveWidth(fWaveWidth), - mfWaveHeight(fWaveHeight) + else { - if(mfWaveWidth < 0.0) - { - mfWaveWidth = 0.0; - } - - if(mfWaveHeight < 0.0) - { - mfWaveHeight = 0.0; - } + // for all other B2DLINEJOIN_* get the range from the base geometry + // and expand by half the line width. + maBufferedRange = getB2DPolygon().getB2DRange(); + maBufferedRange.grow(getLineAttribute().getWidth() * 0.5); } - PolygonWavePrimitive2D::PolygonWavePrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute, - double fWaveWidth, - double fWaveHeight) - : PolygonStrokePrimitive2D(rPolygon, rLineAttribute), - mfWaveWidth(fWaveWidth), - mfWaveHeight(fWaveHeight) - { - if(mfWaveWidth < 0.0) - { - mfWaveWidth = 0.0; - } + return maBufferedRange; + } - if(mfWaveHeight < 0.0) - { - mfWaveHeight = 0.0; - } - } + // It is a hairline, thus the line width is view-dependent. Get range of polygon + // as base size. + // CAUTION: Since a hairline *is* view-dependent, + // - either use maBufferedRange, additionally remember view-dependent + // factor & reset if that changes + // - or do not buffer for hairline -> not really needed, the range is buffered + // in the B2DPolygon, no decomposition is needed and a simple grow is cheap + basegfx::B2DRange aHairlineRange = getB2DPolygon().getB2DRange(); - bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + if (!aHairlineRange.isEmpty()) + { + // Calculate view-dependent hairline width + const basegfx::B2DVector aDiscreteSize( + rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)); + const double fDiscreteHalfLineWidth(aDiscreteSize.getLength() * 0.5); + + if (fDiscreteHalfLineWidth > 0.0) { - if(PolygonStrokePrimitive2D::operator==(rPrimitive)) - { - const PolygonWavePrimitive2D& rCompare = static_cast<const PolygonWavePrimitive2D&>(rPrimitive); + aHairlineRange.grow(fDiscreteHalfLineWidth); + } + } - return (getWaveWidth() == rCompare.getWaveWidth() - && getWaveHeight() == rCompare.getWaveHeight()); - } + return aHairlineRange; +} - return false; - } +// provide unique ID +sal_uInt32 PolygonStrokePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D; +} - basegfx::B2DRange PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const - { - // get range of parent - basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation)); +Primitive2DReference PolygonWavePrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (!getB2DPolygon().count()) + return nullptr; - // if WaveHeight, grow by it - if(basegfx::fTools::more(getWaveHeight(), 0.0)) - { - aRetval.grow(getWaveHeight()); - } + const bool bHasWidth(!basegfx::fTools::equalZero(getWaveWidth())); + const bool bHasHeight(!basegfx::fTools::equalZero(getWaveHeight())); - // if line width, grow by it - if(basegfx::fTools::more(getLineAttribute().getWidth(), 0.0)) - { - aRetval.grow(getLineAttribute().getWidth() * 0.5); - } + if (bHasWidth && bHasHeight) + { + // create waveline curve + basegfx::B2DPolygon aWaveline( + basegfx::utils::createWaveline(getB2DPolygon(), getWaveWidth(), getWaveHeight())); + return new PolygonStrokePrimitive2D(std::move(aWaveline), getLineAttribute(), + getStrokeAttribute()); + } + else + { + // flat waveline, decompose to simple line primitive + return new PolygonStrokePrimitive2D(getB2DPolygon(), getLineAttribute(), + getStrokeAttribute()); + } +} + +PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon, + const attribute::LineAttribute& rLineAttribute, + const attribute::StrokeAttribute& rStrokeAttribute, + double fWaveWidth, double fWaveHeight) + : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute) + , mfWaveWidth(fWaveWidth) + , mfWaveHeight(fWaveHeight) +{ + if (mfWaveWidth < 0.0) + { + mfWaveWidth = 0.0; + } - return aRetval; - } + if (mfWaveHeight < 0.0) + { + mfWaveHeight = 0.0; + } +} + +PolygonWavePrimitive2D::PolygonWavePrimitive2D(const basegfx::B2DPolygon& rPolygon, + const attribute::LineAttribute& rLineAttribute, + double fWaveWidth, double fWaveHeight) + : PolygonStrokePrimitive2D(rPolygon, rLineAttribute) + , mfWaveWidth(fWaveWidth) + , mfWaveHeight(fWaveHeight) +{ + if (mfWaveWidth < 0.0) + { + mfWaveWidth = 0.0; + } - // provide unique ID - ImplPrimitive2DIDBlock(PolygonWavePrimitive2D, PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D) + if (mfWaveHeight < 0.0) + { + mfWaveHeight = 0.0; + } +} +bool PolygonWavePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (PolygonStrokePrimitive2D::operator==(rPrimitive)) + { + const PolygonWavePrimitive2D& rCompare + = static_cast<const PolygonWavePrimitive2D&>(rPrimitive); - void PolygonStrokeArrowPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const - { - // copy local polygon, it may be changed - basegfx::B2DPolygon aLocalPolygon(getB2DPolygon()); - aLocalPolygon.removeDoublePoints(); - basegfx::B2DPolyPolygon aArrowA; - basegfx::B2DPolyPolygon aArrowB; + return (getWaveWidth() == rCompare.getWaveWidth() + && getWaveHeight() == rCompare.getWaveHeight()); + } - if(!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1) - { - // apply arrows - const double fPolyLength(basegfx::utils::getLength(aLocalPolygon)); - double fStart(0.0); - double fEnd(0.0); - double fStartOverlap(0.0); - double fEndOverlap(0.0); - - if(!getStart().isDefault() && getStart().isActive()) - { - // create start arrow primitive and consume - aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(), - fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart); - - // create some overlapping, compromise between straight and peaked markers - // for marker width 0.3cm and marker line width 0.02cm - fStartOverlap = getStart().getWidth() / 15.0; - } - - if(!getEnd().isDefault() && getEnd().isActive()) - { - // create end arrow primitive and consume - aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(), - fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd); - - // create some overlapping - fEndOverlap = getEnd().getWidth() / 15.0; - } - - if(0.0 != fStart || 0.0 != fEnd) - { - // build new poly, consume something from old poly - aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart-fStartOverlap, fPolyLength - fEnd + fEndOverlap, fPolyLength); - } - } + return false; +} - // add shaft - rContainer.push_back(new - PolygonStrokePrimitive2D( - aLocalPolygon, getLineAttribute(), getStrokeAttribute())); +basegfx::B2DRange +PolygonWavePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // get range of parent + basegfx::B2DRange aRetval(PolygonStrokePrimitive2D::getB2DRange(rViewInformation)); - if(aArrowA.count()) - { - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - aArrowA, getLineAttribute().getColor())); - } + // if WaveHeight, grow by it + if (getWaveHeight() > 0.0) + { + aRetval.grow(getWaveHeight()); + } - if(aArrowB.count()) - { - rContainer.push_back( - new PolyPolygonColorPrimitive2D( - aArrowB, getLineAttribute().getColor())); - } - } + // if line width, grow by it + if (getLineAttribute().getWidth() > 0.0) + { + aRetval.grow(getLineAttribute().getWidth() * 0.5); + } - PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute, - const attribute::StrokeAttribute& rStrokeAttribute, - const attribute::LineStartEndAttribute& rStart, - const attribute::LineStartEndAttribute& rEnd) - : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute), - maStart(rStart), - maEnd(rEnd) - { - } + return aRetval; +} + +// provide unique ID +sal_uInt32 PolygonWavePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D; +} - PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D( - const basegfx::B2DPolygon& rPolygon, - const attribute::LineAttribute& rLineAttribute, - const attribute::LineStartEndAttribute& rStart, - const attribute::LineStartEndAttribute& rEnd) - : PolygonStrokePrimitive2D(rPolygon, rLineAttribute), - maStart(rStart), - maEnd(rEnd) +Primitive2DReference PolygonStrokeArrowPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // copy local polygon, it may be changed + basegfx::B2DPolygon aLocalPolygon(getB2DPolygon()); + aLocalPolygon.removeDoublePoints(); + basegfx::B2DPolyPolygon aArrowA; + basegfx::B2DPolyPolygon aArrowB; + + if (!aLocalPolygon.isClosed() && aLocalPolygon.count() > 1) + { + // apply arrows + const double fPolyLength(basegfx::utils::getLength(aLocalPolygon)); + double fStart(0.0); + double fEnd(0.0); + double fStartOverlap(0.0); + double fEndOverlap(0.0); + + if (!getStart().isDefault() && getStart().isActive()) { + // create start arrow primitive and consume + aArrowA = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, getStart().getB2DPolyPolygon(), true, getStart().getWidth(), + fPolyLength, getStart().isCentered() ? 0.5 : 0.0, &fStart); + + // create some overlapping, compromise between straight and peaked markers + // for marker width 0.3cm and marker line width 0.02cm + fStartOverlap = getStart().getWidth() / 15.0; } - bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + if (!getEnd().isDefault() && getEnd().isActive()) { - if(PolygonStrokePrimitive2D::operator==(rPrimitive)) - { - const PolygonStrokeArrowPrimitive2D& rCompare = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive); - - return (getStart() == rCompare.getStart() - && getEnd() == rCompare.getEnd()); - } + // create end arrow primitive and consume + aArrowB = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, getEnd().getB2DPolyPolygon(), false, getEnd().getWidth(), + fPolyLength, getEnd().isCentered() ? 0.5 : 0.0, &fEnd); - return false; + // create some overlapping + fEndOverlap = getEnd().getWidth() / 15.0; } - basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const + if (0.0 != fStart || 0.0 != fEnd) { - if(getStart().isActive() || getEnd().isActive()) - { - // use decomposition when line start/end is used - return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); - } - else - { - // get range from parent - return PolygonStrokePrimitive2D::getB2DRange(rViewInformation); - } + // build new poly, consume something from old poly + aLocalPolygon + = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart - fStartOverlap, + fPolyLength - fEnd + fEndOverlap, fPolyLength); } + } + + // add shaft + Primitive2DContainer aContainer; + aContainer.push_back(new PolygonStrokePrimitive2D(std::move(aLocalPolygon), getLineAttribute(), + getStrokeAttribute())); + + if (aArrowA.count()) + { + aContainer.push_back( + new PolyPolygonColorPrimitive2D(std::move(aArrowA), getLineAttribute().getColor())); + } + + if (aArrowB.count()) + { + aContainer.push_back( + new PolyPolygonColorPrimitive2D(std::move(aArrowB), getLineAttribute().getColor())); + } + return new GroupPrimitive2D(std::move(aContainer)); +} + +PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D( + const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute, + const attribute::StrokeAttribute& rStrokeAttribute, + const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd) + : PolygonStrokePrimitive2D(rPolygon, rLineAttribute, rStrokeAttribute) + , maStart(rStart) + , maEnd(rEnd) +{ +} + +PolygonStrokeArrowPrimitive2D::PolygonStrokeArrowPrimitive2D( + const basegfx::B2DPolygon& rPolygon, const attribute::LineAttribute& rLineAttribute, + const attribute::LineStartEndAttribute& rStart, const attribute::LineStartEndAttribute& rEnd) + : PolygonStrokePrimitive2D(rPolygon, rLineAttribute) + , maStart(rStart) + , maEnd(rEnd) +{ +} - // provide unique ID - ImplPrimitive2DIDBlock(PolygonStrokeArrowPrimitive2D, PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D) +bool PolygonStrokeArrowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (PolygonStrokePrimitive2D::operator==(rPrimitive)) + { + const PolygonStrokeArrowPrimitive2D& rCompare + = static_cast<const PolygonStrokeArrowPrimitive2D&>(rPrimitive); + return (getStart() == rCompare.getStart() && getEnd() == rCompare.getEnd()); } + + return false; +} + +basegfx::B2DRange PolygonStrokeArrowPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& rViewInformation) const +{ + if (getStart().isActive() || getEnd().isActive()) + { + // use decomposition when line start/end is used + return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation); + } + else + { + // get range from parent + return PolygonStrokePrimitive2D::getB2DRange(rViewInformation); + } +} + +// provide unique ID +sal_uInt32 PolygonStrokeArrowPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D; +} + } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive2d/primitivetools2d.cxx b/drawinglayer/source/primitive2d/primitivetools2d.cxx index da0a378a0089..cf98fd9b26c0 100644 --- a/drawinglayer/source/primitive2d/primitivetools2d.cxx +++ b/drawinglayer/source/primitive2d/primitivetools2d.cxx @@ -26,19 +26,17 @@ namespace drawinglayer::primitive2d { void DiscreteMetricDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - // get the current DiscreteUnit, look at X and Y and use the maximum const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); const double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY()))); - if(!getBuffered2DDecomposition().empty() && !basegfx::fTools::equal(fDiscreteUnit, getDiscreteUnit())) + if(hasBuffered2DDecomposition() && !basegfx::fTools::equal(fDiscreteUnit, getDiscreteUnit())) { // conditions of last local decomposition have changed, delete - const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< DiscreteMetricDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember new valid DiscreteUnit const_cast< DiscreteMetricDependentPrimitive2D* >(this)->mfDiscreteUnit = fDiscreteUnit; @@ -53,18 +51,16 @@ namespace drawinglayer::primitive2d void ViewportDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - // get the current Viewport const basegfx::B2DRange& rViewport = rViewInformation.getViewport(); - if(!getBuffered2DDecomposition().empty() && !rViewport.equal(getViewport())) + if(hasBuffered2DDecomposition() && !rViewport.equal(getViewport())) { // conditions of last local decomposition have changed, delete - const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ViewportDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember new valid DiscreteUnit const_cast< ViewportDependentPrimitive2D* >(this)->maViewport = rViewport; @@ -76,18 +72,16 @@ namespace drawinglayer::primitive2d void ViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - // get the current ViewTransformation const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation(); - if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation()) + if(hasBuffered2DDecomposition() && rViewTransformation != getViewTransformation()) { // conditions of last local decomposition have changed, delete - const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember new valid ViewTransformation const_cast< ViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation; @@ -99,27 +93,25 @@ namespace drawinglayer::primitive2d void ObjectAndViewTransformationDependentPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - // get the current ViewTransformation const basegfx::B2DHomMatrix& rViewTransformation = rViewInformation.getViewTransformation(); - if(!getBuffered2DDecomposition().empty() && rViewTransformation != getViewTransformation()) + if(hasBuffered2DDecomposition() && rViewTransformation != getViewTransformation()) { // conditions of last local decomposition have changed, delete - const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } // get the current ObjectTransformation const basegfx::B2DHomMatrix& rObjectTransformation = rViewInformation.getObjectTransformation(); - if(!getBuffered2DDecomposition().empty() && rObjectTransformation != getObjectTransformation()) + if(hasBuffered2DDecomposition() && rObjectTransformation != getObjectTransformation()) { // conditions of last local decomposition have changed, delete - const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { // remember new valid ViewTransformation, and ObjectTransformation const_cast< ObjectAndViewTransformationDependentPrimitive2D* >(this)->maViewTransformation = rViewTransformation; diff --git a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx index e58c05e406af..b57f0fabbdf8 100644 --- a/drawinglayer/source/primitive2d/sceneprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/sceneprimitive2d.cxx @@ -23,25 +23,30 @@ #include <basegfx/matrix/b2dhommatrix.hxx> #include <drawinglayer/attribute/sdrlightattribute3d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <processor3d/zbufferprocessor3d.hxx> #include <processor3d/shadow3dextractor.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <svtools/optionsdrawinglayer.hxx> #include <processor3d/geometry2dextractor.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <basegfx/raster/bzpixelraster.hxx> +#include <utility> #include <vcl/BitmapTools.hxx> +#include <vcl/skia/SkiaHelper.hxx> #include <comphelper/threadpool.hxx> +#include <comphelper/lok.hxx> #include <toolkit/helper/vclunohelper.hxx> +#include <officecfg/Office/Common.hxx> using namespace com::sun::star; namespace { - BitmapEx BPixelRasterToBitmapEx(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize) + Bitmap BPixelRasterToBitmap(const basegfx::BZPixelRaster& rRaster, sal_uInt16 mnAntiAlialize) { - BitmapEx aRetval; + Bitmap aRetval; const sal_uInt32 nWidth(mnAntiAlialize ? rRaster.getWidth()/mnAntiAlialize : rRaster.getWidth()); const sal_uInt32 nHeight(mnAntiAlialize ? rRaster.getHeight()/mnAntiAlialize : rRaster.getHeight()); @@ -61,7 +66,7 @@ namespace sal_uInt16 nRed(0); sal_uInt16 nGreen(0); sal_uInt16 nBlue(0); - sal_uInt16 nOpacity(0); + sal_uInt16 nAlpha(0); sal_uInt32 nIndex(rRaster.getIndexFromXY(x * mnAntiAlialize, y * mnAntiAlialize)); for(sal_uInt32 c(0); c < mnAntiAlialize; c++) @@ -69,27 +74,27 @@ namespace for(sal_uInt32 d(0); d < mnAntiAlialize; d++) { const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++)); - nRed = nRed + rPixel.getRed(); - nGreen = nGreen + rPixel.getGreen(); - nBlue = nBlue + rPixel.getBlue(); - nOpacity = nOpacity + rPixel.getOpacity(); + nRed += rPixel.getRed(); + nGreen += rPixel.getGreen(); + nBlue += rPixel.getBlue(); + nAlpha += rPixel.getAlpha(); } nIndex += rRaster.getWidth() - mnAntiAlialize; } - nOpacity = nOpacity / nDivisor; + nAlpha /= nDivisor; - if(nOpacity) + if(nAlpha) { - aContent.SetPixel(y, x, Color( - 255 - static_cast<sal_uInt8>(nOpacity), + aContent.SetPixel(y, x, Color(ColorAlpha, + static_cast<sal_uInt8>(nAlpha), static_cast<sal_uInt8>(nRed / nDivisor), static_cast<sal_uInt8>(nGreen / nDivisor), static_cast<sal_uInt8>(nBlue / nDivisor) )); } else - aContent.SetPixel(y, x, Color(255, 0, 0, 0)); + aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0)); } } } @@ -103,12 +108,12 @@ namespace { const basegfx::BPixel& rPixel(rRaster.getBPixel(nIndex++)); - if(rPixel.getOpacity()) + if(rPixel.getAlpha()) { - aContent.SetPixel(y, x, Color(255 - rPixel.getOpacity(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue())); + aContent.SetPixel(y, x, Color(ColorAlpha, rPixel.getAlpha(), rPixel.getRed(), rPixel.getGreen(), rPixel.getBlue())); } else - aContent.SetPixel(y, x, Color(255, 0, 0, 0)); + aContent.SetPixel(y, x, Color(ColorAlpha, 0, 0, 0, 0)); } } } @@ -128,8 +133,6 @@ namespace drawinglayer::primitive2d { bool ScenePrimitive2D::impGetShadow3D() const { - ::osl::MutexGuard aGuard( m_aMutex ); - // create on demand if(!mbShadow3DChecked && !getChildren3D().empty()) { @@ -211,8 +214,9 @@ namespace drawinglayer::primitive2d } } - void ScenePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + Primitive2DReference ScenePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const { + Primitive2DContainer aContainer; // create 2D shadows from contained 3D primitives. This creates the shadow primitives on demand and tells if // there are some or not. Do this at start, the shadow might still be visible even when the scene is not if(impGetShadow3D()) @@ -225,7 +229,7 @@ namespace drawinglayer::primitive2d if(aViewRange.isEmpty() || aShadow2DRange.overlaps(aViewRange)) { // add extracted 2d shadows (before 3d scene creations itself) - rContainer.insert(rContainer.end(), maShadowPrimitives.begin(), maShadowPrimitives.end()); + aContainer.append(maShadowPrimitives); } } @@ -237,14 +241,13 @@ namespace drawinglayer::primitive2d calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange); if(aVisibleDiscreteRange.isEmpty()) - return; + return new GroupPrimitive2D(std::move(aContainer)); // test if discrete view size (pixel) maybe too big and limit it double fViewSizeX(aVisibleDiscreteRange.getWidth()); double fViewSizeY(aVisibleDiscreteRange.getHeight()); const double fViewVisibleArea(fViewSizeX * fViewSizeY); - const SvtOptionsDrawinglayer aDrawinglayerOpt; - const double fMaximumVisibleArea(aDrawinglayerOpt.GetQuadratic3DRenderLimit()); + const double fMaximumVisibleArea(officecfg::Office::Common::Drawinglayer::Quadratic3DRenderLimit::get()); double fReduceFactor(1.0); if(fViewVisibleArea > fMaximumVisibleArea) @@ -282,7 +285,7 @@ namespace drawinglayer::primitive2d // determine the oversample value static const sal_uInt16 nDefaultOversampleValue(3); - const sal_uInt16 nOversampleValue(aDrawinglayerOpt.IsAntiAliasing() ? nDefaultOversampleValue : 0); + sal_uInt16 nOversampleValue(SvtOptionsDrawinglayer::IsAntiAliasing() ? nDefaultOversampleValue : 0); geometry::ViewInformation3D aViewInformation3D(getViewInformation3D()); { @@ -354,15 +357,65 @@ namespace drawinglayer::primitive2d const double fLogicY((aInverseOToV * basegfx::B2DVector(0.0, aDiscreteRange.getHeight() * fReduceFactor)).getLength()); // generate ViewSizes - const double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength()); - const double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength()); + double fFullViewSizeX((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(fLogicX, 0.0)).getLength()); + double fFullViewSizeY((rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(0.0, fLogicY)).getLength()); // generate RasterWidth and RasterHeight for visible part - const sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1); - const sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1); + sal_Int32 nRasterWidth(basegfx::fround(fFullViewSizeX * aUnitVisibleRange.getWidth()) + 1); + sal_Int32 nRasterHeight(basegfx::fround(fFullViewSizeY * aUnitVisibleRange.getHeight()) + 1); + + if(!rViewInformation.getReducedDisplayQuality() && comphelper::LibreOfficeKit::isActive()) + { + // for this purpose allow reduced 3D quality and make a compromise + // between quality and speed. This is balanced between those two + // targets, fine-tuning/experimenting can be done with the values + // below. + + // define some values which allow fine-tuning this feature + static const double fMin(80.0); + static const double fSqareMin(fMin * fMin); + static const double fMax(800.0); + static const double fSqareMax(fMax * fMax); + static const double fMaxReduction(0.65); + + // get the square pixels (work on pixel numbers to get same + // behaviour independent of width/height relations) + const double fSquarePixels(nRasterWidth * nRasterHeight); + + if (fSquarePixels > fSqareMin) + { + // only reduce at all when more than fSqareMin pixels needed + double fReduction(fMaxReduction); + + if (fSquarePixels < fSqareMax) + { + // range between fSqareMin and fSqareMax, calculate a + // linear interpolated reduction based on square root + fReduction = sqrt(fSquarePixels); // [fMin .. fMax] + fReduction = fReduction - fMin; // [0 .. (fMax - fMin)] + fReduction = fReduction / (fMax - fMin); // [0 .. 1] + fReduction = 1.0 - (fReduction * (1.0 - fMaxReduction)); // [1 .. fMaxReduction] + + // reduce oversampling for this range + if(nOversampleValue > 2) + nOversampleValue--; + } + else + { + // more than fSqareMax pixels, disable oversampling + nOversampleValue = 0; + } + + // adapt needed values to reduction + nRasterWidth = basegfx::fround(fReduction * nRasterWidth); + nRasterHeight = basegfx::fround(fReduction * nRasterHeight); + fFullViewSizeX *= fReduction; + fFullViewSizeY *= fReduction; + } + } if(!(nRasterWidth && nRasterHeight)) - return; + return new GroupPrimitive2D(std::move(aContainer)); // create view unit buffer basegfx::BZPixelRaster aBZPixelRaster( @@ -370,7 +423,10 @@ namespace drawinglayer::primitive2d nOversampleValue ? nRasterHeight * nOversampleValue : nRasterHeight); // check for parallel execution possibilities - static bool bMultithreadAllowed = false; // loplugin:constvars:ignore + // Skia does not like being touched from multiple threads + // at the same time, and methods like DefaultProcessor3D::impRenderBitmapTexturePrimitive3D + // are going to do that. + static bool bMultithreadAllowed = !SkiaHelper::isVCLSkiaEnabled(); // loplugin:constvars:ignore sal_Int32 nThreadCount(0); comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool()); @@ -437,7 +493,7 @@ namespace drawinglayer::primitive2d } else { - // use default 3D primitive processor to create BitmapEx for aUnitVisiblePart and process + // use default 3D primitive processor to create Bitmap for aUnitVisiblePart and process processor3d::ZBufferProcessor3D aZBufferProcessor3D( aViewInformation3D, getSdrSceneAttribute(), @@ -454,11 +510,11 @@ namespace drawinglayer::primitive2d aZBufferProcessor3D.finish(); } - const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmapEx(aBZPixelRaster, nOversampleValue); + const_cast< ScenePrimitive2D* >(this)->maOldRenderedBitmap = BPixelRasterToBitmap(aBZPixelRaster, nOversampleValue); const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel()); if(!(aBitmapSizePixel.getWidth() && aBitmapSizePixel.getHeight())) - return; + return new GroupPrimitive2D(std::move(aContainer)); // create transform for the created bitmap in discrete coordinates first. basegfx::B2DHomMatrix aNew2DTransform; @@ -472,9 +528,9 @@ namespace drawinglayer::primitive2d aNew2DTransform *= aInverseOToV; // create bitmap primitive and add - rContainer.push_back( + aContainer.push_back( new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(maOldRenderedBitmap), + maOldRenderedBitmap, aNew2DTransform)); // test: Allow to add an outline in the debugger when tests are needed @@ -484,8 +540,9 @@ namespace drawinglayer::primitive2d { basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon()); aOutline.transform(aNew2DTransform); - rContainer.push_back(new PolygonHairlinePrimitive2D(aOutline, basegfx::BColor(1.0, 0.0, 0.0))); + aContainer.push_back(new PolygonHairlinePrimitive2D(std::move(aOutline), basegfx::BColor(1.0, 0.0, 0.0))); } + return new GroupPrimitive2D(std::move(aContainer)); } Primitive2DContainer ScenePrimitive2D::getGeometry2D() const @@ -526,64 +583,61 @@ namespace drawinglayer::primitive2d bool ScenePrimitive2D::tryToCheckLastVisualisationDirectHit(const basegfx::B2DPoint& rLogicHitPoint, bool& o_rResult) const { - if(!maOldRenderedBitmap.IsEmpty() && !maOldUnitVisiblePart.isEmpty()) - { - basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation()); - aInverseSceneTransform.invert(); - const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint); - - if(maOldUnitVisiblePart.isInside(aRelativePoint)) - { - // calculate coordinates relative to visualized part - double fDivisorX(maOldUnitVisiblePart.getWidth()); - double fDivisorY(maOldUnitVisiblePart.getHeight()); + if(maOldRenderedBitmap.IsEmpty() || maOldUnitVisiblePart.isEmpty()) + return false; - if(basegfx::fTools::equalZero(fDivisorX)) - { - fDivisorX = 1.0; - } + basegfx::B2DHomMatrix aInverseSceneTransform(getObjectTransformation()); + aInverseSceneTransform.invert(); + const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rLogicHitPoint); - if(basegfx::fTools::equalZero(fDivisorY)) - { - fDivisorY = 1.0; - } + if(!maOldUnitVisiblePart.isInside(aRelativePoint)) + return false; - const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX); - const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY); + // calculate coordinates relative to visualized part + double fDivisorX(maOldUnitVisiblePart.getWidth()); + double fDivisorY(maOldUnitVisiblePart.getHeight()); - // combine with real BitmapSizePixel to get bitmap coordinates - const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel()); - const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width())); - const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height())); + if(basegfx::fTools::equalZero(fDivisorX)) + { + fDivisorX = 1.0; + } - // try to get a statement about transparency in that pixel - o_rResult = (0xff != maOldRenderedBitmap.GetTransparency(nX, nY)); - return true; - } + if(basegfx::fTools::equalZero(fDivisorY)) + { + fDivisorY = 1.0; } - return false; + const double fRelativeX((aRelativePoint.getX() - maOldUnitVisiblePart.getMinX()) / fDivisorX); + const double fRelativeY((aRelativePoint.getY() - maOldUnitVisiblePart.getMinY()) / fDivisorY); + + // combine with real BitmapSizePixel to get bitmap coordinates + const Size aBitmapSizePixel(maOldRenderedBitmap.GetSizePixel()); + const sal_Int32 nX(basegfx::fround(fRelativeX * aBitmapSizePixel.Width())); + const sal_Int32 nY(basegfx::fround(fRelativeY * aBitmapSizePixel.Height())); + + // try to get a statement about transparency in that pixel + o_rResult = (0 != maOldRenderedBitmap.GetPixelColor(nX, nY).GetAlpha()); + return true; } ScenePrimitive2D::ScenePrimitive2D( - const primitive3d::Primitive3DContainer& rxChildren3D, - const attribute::SdrSceneAttribute& rSdrSceneAttribute, - const attribute::SdrLightingAttribute& rSdrLightingAttribute, - const basegfx::B2DHomMatrix& rObjectTransformation, - const geometry::ViewInformation3D& rViewInformation3D) + primitive3d::Primitive3DContainer aChildren3D, + attribute::SdrSceneAttribute aSdrSceneAttribute, + attribute::SdrLightingAttribute aSdrLightingAttribute, + basegfx::B2DHomMatrix aObjectTransformation, + geometry::ViewInformation3D aViewInformation3D) : BufferedDecompositionPrimitive2D(), - mxChildren3D(rxChildren3D), - maSdrSceneAttribute(rSdrSceneAttribute), - maSdrLightingAttribute(rSdrLightingAttribute), - maObjectTransformation(rObjectTransformation), - maViewInformation3D(rViewInformation3D), - maShadowPrimitives(), + mxChildren3D(std::move(aChildren3D)), + maSdrSceneAttribute(std::move(aSdrSceneAttribute)), + maSdrLightingAttribute(std::move(aSdrLightingAttribute)), + maObjectTransformation(std::move(aObjectTransformation)), + maViewInformation3D(std::move(aViewInformation3D)), mbShadow3DChecked(false), mfOldDiscreteSizeX(0.0), - mfOldDiscreteSizeY(0.0), - maOldUnitVisiblePart(), - maOldRenderedBitmap() + mfOldDiscreteSizeY(0.0) { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } bool ScenePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const @@ -631,15 +685,13 @@ namespace drawinglayer::primitive2d void ScenePrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); - // get the involved ranges (see helper method calculateDiscreteSizes for details) basegfx::B2DRange aDiscreteRange; basegfx::B2DRange aUnitVisibleRange; bool bNeedNewDecomposition(false); bool bDiscreteSizesAreCalculated(false); - if(!getBuffered2DDecomposition().empty()) + if(hasBuffered2DDecomposition()) { basegfx::B2DRange aVisibleDiscreteRange; calculateDiscreteSizes(rViewInformation, aDiscreteRange, aVisibleDiscreteRange, aUnitVisibleRange); @@ -667,10 +719,10 @@ namespace drawinglayer::primitive2d if(bNeedNewDecomposition) { // conditions of last local decomposition have changed, delete - const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + const_cast< ScenePrimitive2D* >(this)->setBuffered2DDecomposition(nullptr); } - if(getBuffered2DDecomposition().empty()) + if(!hasBuffered2DDecomposition()) { if(!bDiscreteSizesAreCalculated) { @@ -690,7 +742,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(ScenePrimitive2D, PRIMITIVE2D_ID_SCENEPRIMITIVE2D) + sal_uInt32 ScenePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SCENEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx index 5080a92a8cd6..7768c1f6af2b 100644 --- a/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx +++ b/drawinglayer/source/primitive2d/sdrdecompositiontools2d.cxx @@ -84,7 +84,7 @@ namespace drawinglayer::primitive2d if(bFilled) { xReference = new PolyPolygonColorPrimitive2D( - aScaledOutline, + std::move(aScaledOutline), basegfx::BColor(0.0, 0.0, 0.0)); } else @@ -92,13 +92,12 @@ namespace drawinglayer::primitive2d const basegfx::BColor aGrayTone(0xc0 / 255.0, 0xc0 / 255.0, 0xc0 / 255.0); xReference = new PolyPolygonHairlinePrimitive2D( - aScaledOutline, + std::move(aScaledOutline), aGrayTone); } // create HiddenGeometryPrimitive2D - return Primitive2DReference( - new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference })); + return new HiddenGeometryPrimitive2D(Primitive2DContainer { xReference }); } } // end of namespace diff --git a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx index e2c0be36ec9e..c364523b076c 100644 --- a/drawinglayer/source/primitive2d/shadowprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/shadowprimitive2d.cxx @@ -22,70 +22,382 @@ #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <drawinglayer/converters.hxx> +#include "GlowSoftEgdeShadowTools.hxx" + +#ifdef DBG_UTIL +#include <o3tl/environment.hxx> +#include <tools/stream.hxx> +#include <vcl/filter/PngImageWriter.hxx> +#endif -#include <sal/log.hxx> #include <memory> +#include <utility> using namespace com::sun::star; - namespace drawinglayer::primitive2d { - ShadowPrimitive2D::ShadowPrimitive2D( - const basegfx::B2DHomMatrix& rShadowTransform, - const basegfx::BColor& rShadowColor, - double fShadowBlur, - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren), - maShadowTransform(rShadowTransform), - maShadowColor(rShadowColor), - mfShadowBlur(fShadowBlur) +ShadowPrimitive2D::ShadowPrimitive2D(basegfx::B2DHomMatrix aShadowTransform, + const basegfx::BColor& rShadowColor, double fShadowBlur, + Primitive2DContainer&& aChildren) + : BufferedDecompositionGroupPrimitive2D(std::move(aChildren)) + , maShadowTransform(std::move(aShadowTransform)) + , maShadowColor(rShadowColor) + , mfShadowBlur(fShadowBlur) + , mfLastDiscreteBlurRadius(0.0) + , maLastClippedRange() +{ + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); +} + +bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive)) + { + const ShadowPrimitive2D& rCompare = static_cast<const ShadowPrimitive2D&>(rPrimitive); + + return (getShadowTransform() == rCompare.getShadowTransform() + && getShadowColor() == rCompare.getShadowColor() + && getShadowBlur() == rCompare.getShadowBlur()); + } + + return false; +} + +// Helper to get the to-be-shadowed geometry completely embedded to +// a ModifiedColorPrimitive2D (change to ShadowColor) and TransformPrimitive2D +// (direction/offset/transformation of shadow). Since this is used pretty +// often, pack into a helper +void ShadowPrimitive2D::getFullyEmbeddedShadowPrimitives(Primitive2DContainer& rContainer) const +{ + if (getChildren().empty()) + return; + + // create a modifiedColorPrimitive containing the shadow color and the content + basegfx::BColorModifierSharedPtr aBColorModifier + = std::make_shared<basegfx::BColorModifier_replace>(getShadowColor()); + Primitive2DReference xRefA(new ModifiedColorPrimitive2D(Primitive2DContainer(getChildren()), + std::move(aBColorModifier))); + Primitive2DContainer aSequenceB{ xRefA }; + + // build transformed primitiveVector with shadow offset and add to target + rContainer.visit(new TransformPrimitive2D(getShadowTransform(), std::move(aSequenceB))); +} + +bool ShadowPrimitive2D::prepareValuesAndcheckValidity( + basegfx::B2DRange& rBlurRange, basegfx::B2DRange& rClippedRange, + basegfx::B2DVector& rDiscreteBlurSize, double& rfDiscreteBlurRadius, + const geometry::ViewInformation2D& rViewInformation) const +{ + // no BlurRadius defined, done + if (getShadowBlur() <= 0.0) + return false; + + // no geometry, done + if (getChildren().empty()) + return false; + + // no pixel target, done + if (rViewInformation.getObjectToViewTransformation().isIdentity()) + return false; + + // get fully embedded ShadowPrimitive + Primitive2DContainer aEmbedded; + getFullyEmbeddedShadowPrimitives(aEmbedded); + + // get geometry range that defines area that needs to be pixelated + rBlurRange = aEmbedded.getB2DRange(rViewInformation); + + // no range of geometry, done + if (rBlurRange.isEmpty()) + return false; + + // extend range by BlurRadius in all directions + rBlurRange.grow(getShadowBlur()); + + // initialize ClippedRange to full BlurRange -> all is visible + rClippedRange = rBlurRange; + + // get Viewport and check if used. If empty, all is visible (see + // ViewInformation2D definition in viewinformation2d.hxx) + if (!rViewInformation.getViewport().isEmpty()) + { + // if used, extend by BlurRadius to ensure needed parts are included + basegfx::B2DRange aVisibleArea(rViewInformation.getViewport()); + aVisibleArea.grow(getShadowBlur()); + + // calculate ClippedRange + rClippedRange.intersect(aVisibleArea); + + // if BlurRange is completely outside of VisibleArea, ClippedRange + // will be empty and we are done + if (rClippedRange.isEmpty()) + return false; + } + + // calculate discrete pixel size of BlurRange. If it's too small to visualize, we are done + rDiscreteBlurSize = rViewInformation.getObjectToViewTransformation() * rBlurRange.getRange(); + if (ceil(rDiscreteBlurSize.getX()) < 2.0 || ceil(rDiscreteBlurSize.getY()) < 2.0) + return false; + + // calculate discrete pixel size of BlurRadius. If it's too small to visualize, we are done + rfDiscreteBlurRadius = ceil( + (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getShadowBlur(), 0)) + .getLength()); + if (rfDiscreteBlurRadius < 1.0) + return false; + + return true; +} + +void ShadowPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const +{ + if (getShadowBlur() <= 0.0) + { + // Normal (non-blurred) shadow is already completely + // handled by get2DDecomposition and not buffered. It + // does not need to be since it's a simple embedding + // to a ModifiedColorPrimitive2D and TransformPrimitive2D + return; + } + + // from here on we process a blurred shadow + basegfx::B2DRange aBlurRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteBlurSize; + double fDiscreteBlurRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, aDiscreteBlurSize, + fDiscreteBlurRadius, rViewInformation)) + return; + + // Create embedding transformation from object to top-left zero-aligned + // target pixel geometry (discrete form of ClippedRange) + // First, move to top-left of BlurRange + const sal_uInt32 nDiscreteBlurWidth(ceil(aDiscreteBlurSize.getX())); + const sal_uInt32 nDiscreteBlurHeight(ceil(aDiscreteBlurSize.getY())); + basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix( + -aClippedRange.getMinX(), -aClippedRange.getMinY())); + // Second, scale to discrete bitmap size + // Even when using the offset from ClippedRange, we need to use the + // scaling from the full representation, thus from BlurRange + aEmbedding.scale(nDiscreteBlurWidth / aBlurRange.getWidth(), + nDiscreteBlurHeight / aBlurRange.getHeight()); + + // Get fully embedded ShadowPrimitives. This will also embed to + // ModifiedColorPrimitive2D (what is not urgently needed) to create + // the alpha channel, but a paint with all colors set to a single + // one (like shadowColor here) is often less expensive due to possible + // simplifications painting the primitives (e.g. gradient) + Primitive2DContainer aEmbedded; + getFullyEmbeddedShadowPrimitives(aEmbedded); + + // Embed content graphics to TransformPrimitive2D + const primitive2d::Primitive2DReference xEmbedRef( + new primitive2d::TransformPrimitive2D(aEmbedding, std::move(aEmbedded))); + primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef }; + + // Create Bitmap using drawinglayer tooling, including a MaximumQuadraticPixel + // limitation to be safe and not go runtime/memory havoc. Use a pretty small + // limit due to this is Blurred Shadow functionality and will look good with bitmap + // scaling anyways. The value of 250.000 square pixels below maybe adapted as needed. + const basegfx::B2DVector aDiscreteClippedSize(rViewInformation.getObjectToViewTransformation() + * aClippedRange.getRange()); + const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX())); + const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY())); + const geometry::ViewInformation2D aViewInformation2D; + const sal_uInt32 nMaximumQuadraticPixels(250000); + + // I have now added a helper that just creates the mask without having + // to render the content, use it, it's faster + const AlphaMask aAlpha(::drawinglayer::createAlphaMask( + std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight, + nMaximumQuadraticPixels)); + + // if we have no shadow, we are done + if (aAlpha.IsEmpty()) + return; + + const Size aBitmapExSizePixel(aAlpha.GetSizePixel()); + if (!(aBitmapExSizePixel.Width() > 0 && aBitmapExSizePixel.Height() > 0)) + return; + + // We may have to take a corrective scaling into account when the + // MaximumQuadraticPixel limit was used/triggered + double fScale(1.0); + + if (static_cast<sal_uInt32>(aBitmapExSizePixel.Width()) != nDiscreteClippedWidth + || static_cast<sal_uInt32>(aBitmapExSizePixel.Height()) != nDiscreteClippedHeight) + { + // scale in X and Y should be the same (see fReduceFactor in createAlphaMask), + // so adapt numerically to a single scale value, they are integer rounded values + const double fScaleX(static_cast<double>(aBitmapExSizePixel.Width()) + / static_cast<double>(nDiscreteClippedWidth)); + const double fScaleY(static_cast<double>(aBitmapExSizePixel.Height()) + / static_cast<double>(nDiscreteClippedHeight)); + + fScale = (fScaleX + fScaleY) * 0.5; + } + + // Use the Alpha as base to blur and apply the effect + const AlphaMask mask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask( + aAlpha, 0, fDiscreteBlurRadius * fScale, 0, false)); + + // The end result is the bitmap filled with blur color and blurred 8-bit alpha mask + Bitmap bmp(aAlpha.GetSizePixel(), vcl::PixelFormat::N24_BPP); + bmp.Erase(Color(getShadowColor())); + Bitmap result(bmp, mask); + +#ifdef DBG_UTIL + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + if (bDoSaveForVisualControl) + { + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) { + SvFileStream aNew(sDumpPath + "test_shadowblur.png", + StreamMode::WRITE | StreamMode::TRUNC); + vcl::PngImageWriter aPNGWriter(aNew); + aPNGWriter.write(result); } + } +#endif + + // Independent from discrete sizes of blur alpha creation, always + // map and project blur result to geometry range extended by blur + // radius, but to the eventually clipped instance (ClippedRange) + const primitive2d::Primitive2DReference xEmbedRefBitmap( + new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix( + aClippedRange.getWidth(), aClippedRange.getHeight(), + aClippedRange.getMinX(), aClippedRange.getMinY()))); + + rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap }; +} + +void ShadowPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + if (getShadowBlur() <= 0.0) + { + // normal (non-blurred) shadow + if (getChildren().empty()) + return; + + // get fully embedded ShadowPrimitives + Primitive2DContainer aEmbedded; + getFullyEmbeddedShadowPrimitives(aEmbedded); - bool ShadowPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + rVisitor.visit(aEmbedded); + return; + } + + // here we have a blurred shadow, check conditions of last + // buffered decompose and decide re-use or re-create by using + // setBuffered2DDecomposition to reset local buffered version + basegfx::B2DRange aBlurRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteBlurSize; + double fDiscreteBlurRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aBlurRange, aClippedRange, aDiscreteBlurSize, + fDiscreteBlurRadius, rViewInformation)) + return; + + if (hasBuffered2DDecomposition()) + { + // First check is to detect if the last created decompose is capable + // to represent the now requested visualization (see similar + // implementation at GlowPrimitive2D). + if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange)) { - if(BasePrimitive2D::operator==(rPrimitive)) - { - const ShadowPrimitive2D& rCompare = static_cast< const ShadowPrimitive2D& >(rPrimitive); + basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange); - return (getShadowTransform() == rCompare.getShadowTransform() - && getShadowColor() == rCompare.getShadowColor() - && getShadowBlur() == rCompare.getShadowBlur()); + if (!rViewInformation.getObjectToViewTransformation().isIdentity()) + { + // Grow by view-dependent size of 1/2 pixel + const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation() + * basegfx::B2DVector(0.5, 0)) + .getLength()); + aLastClippedRangeAndHairline.grow(fHalfPixel); } - return false; + if (!aLastClippedRangeAndHairline.isInside(aClippedRange)) + { + // Conditions of last local decomposition have changed, delete + const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition( + Primitive2DContainer()); + } } + } - basegfx::B2DRange ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const + if (hasBuffered2DDecomposition()) + { + // Second check is to react on changes of the DiscreteSoftRadius when + // zooming in/out (see similar implementation at ShadowPrimitive2D). + bool bFree(mfLastDiscreteBlurRadius <= 0.0 || fDiscreteBlurRadius <= 0.0); + + if (!bFree) { - basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation)); - aRetval.grow(getShadowBlur()); - aRetval.transform(getShadowTransform()); - return aRetval; + const double fDiff(fabs(mfLastDiscreteBlurRadius - fDiscreteBlurRadius)); + const double fLen(fabs(mfLastDiscreteBlurRadius) + fabs(fDiscreteBlurRadius)); + const double fRelativeChange(fDiff / fLen); + + // Use lower fixed values here to change more often, higher to change less often. + // Value is in the range of ]0.0 .. 1.0] + bFree = fRelativeChange >= 0.15; } - void ShadowPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*rViewInformation*/) const + if (bFree) { - if(getChildren().empty()) - return; - - // create a modifiedColorPrimitive containing the shadow color and the content - const basegfx::BColorModifierSharedPtr aBColorModifier = - std::make_shared<basegfx::BColorModifier_replace>( - getShadowColor()); - const Primitive2DReference xRefA( - new ModifiedColorPrimitive2D( - getChildren(), - aBColorModifier)); - const Primitive2DContainer aSequenceB { xRefA }; - - // build transformed primitiveVector with shadow offset and add to target - rVisitor.append(new TransformPrimitive2D(getShadowTransform(), aSequenceB)); + // Conditions of last local decomposition have changed, delete + const_cast<ShadowPrimitive2D*>(this)->setBuffered2DDecomposition( + Primitive2DContainer()); } + } + + if (!hasBuffered2DDecomposition()) + { + // refresh last used DiscreteBlurRadius and ClippedRange to new remembered values + const_cast<ShadowPrimitive2D*>(this)->mfLastDiscreteBlurRadius = fDiscreteBlurRadius; + const_cast<ShadowPrimitive2D*>(this)->maLastClippedRange = aClippedRange; + } + + // call parent, that will check for empty, call create2DDecomposition and + // set as decomposition + BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + +basegfx::B2DRange +ShadowPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily) + // use the decompose - what works, but is not needed here. + // We know the to-be-visualized geometry and the radius it needs to be extended, + // so simply calculate the exact needed range. + basegfx::B2DRange aRetval(getChildren().getB2DRange(rViewInformation)); + + if (getShadowBlur() > 0.0) + { + // blurred shadow, that extends the geometry + aRetval.grow(getShadowBlur()); + } + + aRetval.transform(getShadowTransform()); + return aRetval; +} - // provide unique ID - ImplPrimitive2DIDBlock(ShadowPrimitive2D, PRIMITIVE2D_ID_SHADOWPRIMITIVE2D) +// provide unique ID +sal_uInt32 ShadowPrimitive2D::getPrimitive2DID() const { return PRIMITIVE2D_ID_SHADOWPRIMITIVE2D; } } // end of namespace diff --git a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx index 4c5b1b2c6102..66cbe5490278 100644 --- a/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/softedgeprimitive2d.cxx @@ -18,20 +18,35 @@ */ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <drawinglayer/converters.hxx> +#include "GlowSoftEgdeShadowTools.hxx" + +#ifdef DBG_UTIL +#include <o3tl/environment.hxx> +#include <tools/stream.hxx> +#include <vcl/filter/PngImageWriter.hxx> +#endif namespace drawinglayer::primitive2d { -SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) +SoftEdgePrimitive2D::SoftEdgePrimitive2D(double fRadius, Primitive2DContainer&& aChildren) + : BufferedDecompositionGroupPrimitive2D(std::move(aChildren)) , mfRadius(fRadius) + , mfLastDiscreteSoftRadius(0.0) + , maLastClippedRange() { + // activate callback to flush buffered decomposition content + activateFlushOnTimer(); } bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if (GroupPrimitive2D::operator==(rPrimitive)) + if (BufferedDecompositionGroupPrimitive2D::operator==(rPrimitive)) { auto& rCompare = static_cast<const SoftEdgePrimitive2D&>(rPrimitive); return getRadius() == rCompare.getRadius(); @@ -40,29 +55,305 @@ bool SoftEdgePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const return false; } -void SoftEdgePrimitive2D::get2DDecomposition( - Primitive2DDecompositionVisitor& rVisitor, +bool SoftEdgePrimitive2D::prepareValuesAndcheckValidity( + basegfx::B2DRange& rSoftRange, basegfx::B2DRange& rClippedRange, + basegfx::B2DVector& rDiscreteSoftSize, double& rfDiscreteSoftRadius, const geometry::ViewInformation2D& rViewInformation) const { + // no SoftRadius defined, done + if (getRadius() <= 0.0) + return false; + + // no geometry, done if (getChildren().empty()) + return false; + + // no pixel target, done + if (rViewInformation.getObjectToViewTransformation().isIdentity()) + return false; + + // get geometry range that defines area that needs to be pixelated + rSoftRange = getChildren().getB2DRange(rViewInformation); + + // no range of geometry, done + if (rSoftRange.isEmpty()) + return false; + + // initialize ClippedRange to full SoftRange -> all is visible + rClippedRange = rSoftRange; + + // get Viewport and check if used. If empty, all is visible (see + // ViewInformation2D definition in viewinformation2d.hxx) + if (!rViewInformation.getViewport().isEmpty()) + { + // if used, extend by SoftRadius to ensure needed parts are included + // that are not visible, but influence the visible parts + basegfx::B2DRange aVisibleArea(rViewInformation.getViewport()); + aVisibleArea.grow(getRadius() * 2); + + // To do this correctly, it needs to be done in discrete coordinates. + // The object may be transformed relative to the original# + // ObjectTransformation, e.g. when re-used in shadow + aVisibleArea.transform(rViewInformation.getViewTransformation()); + rClippedRange.transform(rViewInformation.getObjectToViewTransformation()); + + // calculate ClippedRange + rClippedRange.intersect(aVisibleArea); + + // if SoftRange is completely outside of VisibleArea, ClippedRange + // will be empty and we are done + if (rClippedRange.isEmpty()) + return false; + + // convert result back to object coordinates + rClippedRange.transform(rViewInformation.getInverseObjectToViewTransformation()); + } + + // calculate discrete pixel size of SoftRange. If it's too small to visualize, we are done + rDiscreteSoftSize = rViewInformation.getObjectToViewTransformation() * rSoftRange.getRange(); + if (ceil(rDiscreteSoftSize.getX()) < 2.0 || ceil(rDiscreteSoftSize.getY()) < 2.0) + return false; + + // calculate discrete pixel size of SoftRadius. If it's too small to visualize, we are done + rfDiscreteSoftRadius = ceil( + (rViewInformation.getObjectToViewTransformation() * basegfx::B2DVector(getRadius(), 0)) + .getLength()); + if (rfDiscreteSoftRadius < 1.0) + return false; + + return true; +} + +void SoftEdgePrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const +{ + // Use endless while-loop-and-break mechanism due to having multiple + // exit scenarios that all have to do the same thing when exiting + while (true) + { + basegfx::B2DRange aSoftRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteSoftSize; + double fDiscreteSoftRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize, + fDiscreteSoftRadius, rViewInformation)) + break; + + // Create embedding transformation from object to top-left zero-aligned + // target pixel geometry (discrete form of ClippedRange) + // First, move to top-left of SoftRange + const sal_uInt32 nDiscreteSoftWidth(ceil(aDiscreteSoftSize.getX())); + const sal_uInt32 nDiscreteSoftHeight(ceil(aDiscreteSoftSize.getY())); + basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix( + -aClippedRange.getMinX(), -aClippedRange.getMinY())); + // Second, scale to discrete bitmap size + // Even when using the offset from ClippedRange, we need to use the + // scaling from the full representation, thus from SoftRange + aEmbedding.scale(nDiscreteSoftWidth / aSoftRange.getWidth(), + nDiscreteSoftHeight / aSoftRange.getHeight()); + + // Embed content graphics to TransformPrimitive2D + const primitive2d::Primitive2DReference xEmbedRef( + new primitive2d::TransformPrimitive2D(aEmbedding, Primitive2DContainer(getChildren()))); + primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef }; + + // Create Bitmap using drawinglayer tooling, including a MaximumQuadraticPixel + // limitation to be safe and not go runtime/memory havoc. Use a pretty small + // limit due to this is softEdge functionality and will look good with bitmap scaling + // anyways. The value of 250.000 square pixels below maybe adapted as needed. + const basegfx::B2DVector aDiscreteClippedSize( + rViewInformation.getObjectToViewTransformation() * aClippedRange.getRange()); + const sal_uInt32 nDiscreteClippedWidth(ceil(aDiscreteClippedSize.getX())); + const sal_uInt32 nDiscreteClippedHeight(ceil(aDiscreteClippedSize.getY())); + const geometry::ViewInformation2D aViewInformation2D; + const sal_uInt32 nMaximumQuadraticPixels(250000); + // tdf#156808 force an alpha mask to be created even if it has no alpha + // We need an alpha mask, even if it is totally opaque, so that + // drawinglayer::primitive2d::ProcessAndBlurAlphaMask() can be called. + // Otherwise, blurring of edges will fail in cases like running in a + // slideshow or exporting to PDF. + const Bitmap aBitmap(::drawinglayer::convertToBitmap( + std::move(xEmbedSeq), aViewInformation2D, nDiscreteClippedWidth, nDiscreteClippedHeight, + nMaximumQuadraticPixels, true)); + + if (aBitmap.IsEmpty()) + break; + + // Get Bitmap and check size. If no content, we are done + const Size aBitmapSizePixel(aBitmap.GetSizePixel()); + if (!(aBitmapSizePixel.Width() > 0 && aBitmapSizePixel.Height() > 0)) + break; + + // We may have to take a corrective scaling into account when the + // MaximumQuadraticPixel limit was used/triggered + double fScale(1.0); + + if (static_cast<sal_uInt32>(aBitmapSizePixel.Width()) != nDiscreteClippedWidth + || static_cast<sal_uInt32>(aBitmapSizePixel.Height()) != nDiscreteClippedHeight) + { + // scale in X and Y should be the same (see fReduceFactor in convertToBitmapEx), + // so adapt numerically to a single scale value, they are integer rounded values + const double fScaleX(static_cast<double>(aBitmapSizePixel.Width()) + / static_cast<double>(nDiscreteClippedWidth)); + const double fScaleY(static_cast<double>(aBitmapSizePixel.Height()) + / static_cast<double>(nDiscreteClippedHeight)); + + fScale = (fScaleX + fScaleY) * 0.5; + } + + // Get the Alpha and use as base to blur and apply the effect + AlphaMask aMask(aBitmap.CreateAlphaMask()); + if (aMask.IsEmpty()) // There is no mask, fully opaque + break; + AlphaMask blurMask(drawinglayer::primitive2d::ProcessAndBlurAlphaMask( + aMask, -fDiscreteSoftRadius * fScale, fDiscreteSoftRadius * fScale, 0)); + aMask.BlendWith(blurMask); + + // The end result is the original bitmap with blurred 8-bit alpha mask + Bitmap result(aBitmap.CreateColorBitmap(), aMask); + +#ifdef DBG_UTIL + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + if (bDoSaveForVisualControl) + { + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) + { + SvFileStream aNew(sDumpPath + "test_softedge.png", + StreamMode::WRITE | StreamMode::TRUNC); + vcl::PngImageWriter aPNGWriter(aNew); + aPNGWriter.write(result); + } + } +#endif + + // Independent from discrete sizes of soft alpha creation, always + // map and project soft result to geometry range extended by soft + // radius, but to the eventually clipped instance (ClippedRange) + const primitive2d::Primitive2DReference xEmbedRefBitmap( + new BitmapPrimitive2D(result, basegfx::utils::createScaleTranslateB2DHomMatrix( + aClippedRange.getWidth(), aClippedRange.getHeight(), + aClippedRange.getMinX(), aClippedRange.getMinY()))); + + rContainer = primitive2d::Primitive2DContainer{ xEmbedRefBitmap }; + + // we made it, return return; + } - if (!mbInMaskGeneration) + // creation failed for some of many possible reasons, use original + // content, so the unmodified original geometry will be the result, + // just without any softEdge effect + rContainer = getChildren(); +} + +void SoftEdgePrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + // Use endless while-loop-and-break mechanism due to having multiple + // exit scenarios that all have to do the same thing when exiting + while (true) { - GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + basegfx::B2DRange aSoftRange; + basegfx::B2DRange aClippedRange; + basegfx::B2DVector aDiscreteSoftSize; + double fDiscreteSoftRadius(0.0); + + // Check various validity details and calculate/prepare values. If false, we are done + if (!prepareValuesAndcheckValidity(aSoftRange, aClippedRange, aDiscreteSoftSize, + fDiscreteSoftRadius, rViewInformation)) + break; + + if (hasBuffered2DDecomposition()) + { + // First check is to detect if the last created decompose is capable + // to represent the now requested visualization (see similar + // implementation at GlowPrimitive2D). + if (!maLastClippedRange.isEmpty() && !maLastClippedRange.isInside(aClippedRange)) + { + basegfx::B2DRange aLastClippedRangeAndHairline(maLastClippedRange); + + if (!rViewInformation.getObjectToViewTransformation().isIdentity()) + { + // Grow by view-dependent size of 1/2 pixel + const double fHalfPixel((rViewInformation.getInverseObjectToViewTransformation() + * basegfx::B2DVector(0.5, 0)) + .getLength()); + aLastClippedRangeAndHairline.grow(fHalfPixel); + } + + if (!aLastClippedRangeAndHairline.isInside(aClippedRange)) + { + // Conditions of last local decomposition have changed, delete + const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition( + Primitive2DContainer()); + } + } + } + + if (hasBuffered2DDecomposition()) + { + // Second check is to react on changes of the DiscreteSoftRadius when + // zooming in/out (see similar implementation at GlowPrimitive2D). + bool bFree(mfLastDiscreteSoftRadius <= 0.0 || fDiscreteSoftRadius <= 0.0); + + if (!bFree) + { + const double fDiff(fabs(mfLastDiscreteSoftRadius - fDiscreteSoftRadius)); + const double fLen(fabs(mfLastDiscreteSoftRadius) + fabs(fDiscreteSoftRadius)); + const double fRelativeChange(fDiff / fLen); + + // Use a lower value here, soft edge keeps it's content so avoid that it gets too + // unsharp in the pixel visualization + // Value is in the range of ]0.0 .. 1.0] + bFree = fRelativeChange >= 0.075; + } + + if (bFree) + { + // Conditions of last local decomposition have changed, delete + const_cast<SoftEdgePrimitive2D*>(this)->setBuffered2DDecomposition( + Primitive2DContainer()); + } + } + + if (!hasBuffered2DDecomposition()) + { + // refresh last used DiscreteSoftRadius and ClippedRange to new remembered values + const_cast<SoftEdgePrimitive2D*>(this)->mfLastDiscreteSoftRadius = fDiscreteSoftRadius; + const_cast<SoftEdgePrimitive2D*>(this)->maLastClippedRange = aClippedRange; + } + + // call parent, that will check for empty, call create2DDecomposition and + // set as decomposition + BufferedDecompositionGroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + + // we made it, return return; } - // create a modifiedColorPrimitive containing the *black* color and the content. Using black - // on white allows creating useful mask in VclPixelProcessor2D::processSoftEdgePrimitive2D. - basegfx::BColorModifierSharedPtr aBColorModifier - = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor()); + // No soft edge needed for some of many possible reasons, use original content + rVisitor.visit(getChildren()); +} - const Primitive2DReference xRef(new ModifiedColorPrimitive2D(getChildren(), aBColorModifier)); - rVisitor.append(xRef); +basegfx::B2DRange +SoftEdgePrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // Hint: Do *not* use GroupPrimitive2D::getB2DRange, that will (unnecessarily) + // use the decompose - what works, but is not needed here. + // We know the to-be-visualized geometry and the radius it needs to be extended, + // so simply calculate the exact needed range. + return getChildren().getB2DRange(rViewInformation); } -ImplPrimitive2DIDBlock(SoftEdgePrimitive2D, PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D) +sal_uInt32 SoftEdgePrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D; +} } // end of namespace diff --git a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx index c46b7df87fc5..e34a1a2a1f6d 100644 --- a/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/structuretagprimitive2d.cxx @@ -27,15 +27,24 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { StructureTagPrimitive2D::StructureTagPrimitive2D( - const vcl::PDFWriter::StructElement& rStructureElement, + const vcl::pdf::StructElement& rStructureElement, bool bBackground, bool bIsImage, - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren), + bool bIsDecorative, + Primitive2DContainer&& aChildren, + void const*const pAnchorStructureElementKey, + ::std::vector<sal_Int32> const*const pAnnotIds) + : GroupPrimitive2D(std::move(aChildren)), maStructureElement(rStructureElement), mbBackground(bBackground), - mbIsImage(bIsImage) + mbIsImage(bIsImage), + mbIsDecorative(bIsDecorative) + , m_pAnchorStructureElementKey(pAnchorStructureElementKey) { + if (pAnnotIds) + { + m_AnnotIds = *pAnnotIds; + } } bool StructureTagPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const @@ -52,7 +61,17 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(StructureTagPrimitive2D, PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D) + sal_uInt32 StructureTagPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D; + } + + bool StructureTagPrimitive2D::isTaggedSdrObject() const + { + // note at the moment *all* StructureTagPrimitive2D are created for + // SdrObjects - if that ever changes, need another condition here + return !isBackground() || isImage(); + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx index d3e32e22ea46..ea848dd6d21e 100644 --- a/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/svggradientprimitive2d.cxx @@ -27,10 +27,13 @@ #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> +#include <osl/diagnose.h> #include <sal/log.hxx> #include <cmath> - +#include <utility> +#include <vcl/skia/SkiaHelper.hxx> using namespace com::sun::star; @@ -61,45 +64,46 @@ namespace namespace drawinglayer::primitive2d { - void SvgGradientHelper::createSingleGradientEntryFill(Primitive2DContainer& rContainer) const + Primitive2DReference SvgGradientHelper::createSingleGradientEntryFill() const { - const SvgGradientEntryVector& rEntries = getGradientEntries(); + const SvgGradientEntryVector& rEntries(getGradientEntries()); const sal_uInt32 nCount(rEntries.size()); - if(nCount) + if(0 == nCount) { - const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1]; - const double fOpacity(rSingleEntry.getOpacity()); - - if(fOpacity > 0.0) - { - Primitive2DReference xRef( - new PolyPolygonColorPrimitive2D( - getPolyPolygon(), - rSingleEntry.getColor())); - - if(fOpacity < 1.0) - { - const Primitive2DContainer aContent { xRef }; + // no entries, done + OSL_ENSURE(false, "Single gradient entry construction without entry (!)"); + return nullptr; + } - xRef = Primitive2DReference( - new UnifiedTransparencePrimitive2D( - aContent, - 1.0 - fOpacity)); - } + const SvgGradientEntry& rSingleEntry(rEntries[nCount - 1]); + const double fOpacity(rSingleEntry.getOpacity()); - rContainer.push_back(xRef); - } + if (fOpacity <= 0.0 || basegfx::fTools::equalZero(fOpacity)) + { + // completely opaque, done + return nullptr; } - else + + if (basegfx::fTools::moreOrEqual(fOpacity, 1.0)) { - OSL_ENSURE(false, "Single gradient entry construction without entry (!)"); + // no opacity + return Primitive2DReference { + new PolyPolygonColorPrimitive2D( + getPolyPolygon(), + rSingleEntry.getColor()) }; } + + // if transparent, use PolyPolygonRGBAPrimitive2D + return Primitive2DReference { + new PolyPolygonRGBAPrimitive2D( + getPolyPolygon(), + rSingleEntry.getColor(), + 1.0 - fOpacity) }; } void SvgGradientHelper::checkPreconditions() { - mbPreconditionsChecked = true; const SvgGradientEntryVector& rEntries = getGradientEntries(); if(rEntries.empty()) @@ -186,7 +190,7 @@ namespace drawinglayer::primitive2d const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2); const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries()); - for(SvgGradientEntryVector::const_reverse_iterator aIter(rCurrent.rbegin()); aIter != rCurrent.rend(); aIter++) + for(SvgGradientEntryVector::const_reverse_iterator aIter(rCurrent.rbegin()); aIter != rCurrent.rend(); ++aIter) { if(basegfx::fTools::lessOrEqual(aIter->getOffset(), fFrac)) { @@ -208,7 +212,7 @@ namespace drawinglayer::primitive2d const bool bMirror(SpreadMethod::Reflect == getSpreadMethod() && 0 != rInt % 2); const SvgGradientEntryVector& rCurrent(bMirror ? getMirroredGradientEntries() : getGradientEntries()); - for(SvgGradientEntryVector::const_iterator aIter(rCurrent.begin()); aIter != rCurrent.end(); aIter++) + for(SvgGradientEntryVector::const_iterator aIter(rCurrent.begin()); aIter != rCurrent.end(); ++aIter) { if(basegfx::fTools::more(aIter->getOffset(), fFrac)) { @@ -303,26 +307,25 @@ namespace drawinglayer::primitive2d } } - void SvgGradientHelper::createResult( - Primitive2DContainer& rContainer, - const Primitive2DContainer& rTargetColor, - const Primitive2DContainer& rTargetOpacity, + Primitive2DReference SvgGradientHelper::createResult( + Primitive2DContainer aTargetColor, + Primitive2DContainer aTargetOpacity, const basegfx::B2DHomMatrix& rUnitGradientToObject, bool bInvert) const { - const Primitive2DContainer aTargetColorEntries(rTargetColor.maybeInvert(bInvert)); - const Primitive2DContainer aTargetOpacityEntries(rTargetOpacity.maybeInvert(bInvert)); + Primitive2DContainer aTargetColorEntries(aTargetColor.maybeInvert(bInvert)); + Primitive2DContainer aTargetOpacityEntries(aTargetOpacity.maybeInvert(bInvert)); if(aTargetColorEntries.empty()) - return; + return nullptr; Primitive2DReference xRefContent; if(!aTargetOpacityEntries.empty()) { const Primitive2DReference xRefOpacity = new TransparencePrimitive2D( - aTargetColorEntries, - aTargetOpacityEntries); + std::move(aTargetColorEntries), + std::move(aTargetOpacityEntries)); xRefContent = new TransformPrimitive2D( rUnitGradientToObject, @@ -332,28 +335,26 @@ namespace drawinglayer::primitive2d { xRefContent = new TransformPrimitive2D( rUnitGradientToObject, - aTargetColorEntries); + std::move(aTargetColorEntries)); } - rContainer.push_back(new MaskPrimitive2D( + return new MaskPrimitive2D( getPolyPolygon(), - Primitive2DContainer { xRefContent })); + Primitive2DContainer { xRefContent }); } SvgGradientHelper::SvgGradientHelper( - const basegfx::B2DHomMatrix& rGradientTransform, - const basegfx::B2DPolyPolygon& rPolyPolygon, - const SvgGradientEntryVector& rGradientEntries, + basegfx::B2DHomMatrix aGradientTransform, + basegfx::B2DPolyPolygon aPolyPolygon, + SvgGradientEntryVector&& rGradientEntries, const basegfx::B2DPoint& rStart, bool bUseUnitCoordinates, SpreadMethod aSpreadMethod) - : maGradientTransform(rGradientTransform), - maPolyPolygon(rPolyPolygon), - maGradientEntries(rGradientEntries), - maMirroredGradientEntries(), + : maGradientTransform(std::move(aGradientTransform)), + maPolyPolygon(std::move(aPolyPolygon)), + maGradientEntries(std::move(rGradientEntries)), maStart(rStart), maSpreadMethod(aSpreadMethod), - mbPreconditionsChecked(false), mbCreatesContent(false), mbSingleEntry(false), mbFullyOpaque(true), @@ -377,7 +378,7 @@ namespace drawinglayer::primitive2d void SvgGradientHelper::createMirroredGradientEntries() { - if(!(maMirroredGradientEntries.empty() && !getGradientEntries().empty())) + if(!maMirroredGradientEntries.empty() || getGradientEntries().empty()) return; const sal_uInt32 nCount(getGradientEntries().size()); @@ -465,64 +466,66 @@ namespace drawinglayer::primitive2d } } - void SvgLinearGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + basegfx::B2DHomMatrix SvgLinearGradientPrimitive2D::createUnitGradientToObjectTransformation() const { - if(!getPreconditionsChecked()) + const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); + const double fPolyWidth(aPolyRange.getWidth()); + const double fPolyHeight(aPolyRange.getHeight()); + + // create ObjectTransform based on polygon range + const basegfx::B2DHomMatrix aObjectTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix( + fPolyWidth, fPolyHeight, + aPolyRange.getMinX(), aPolyRange.getMinY())); + basegfx::B2DHomMatrix aUnitGradientToObject; + + if(getUseUnitCoordinates()) { - const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions(); + // interpret in unit coordinate system -> object aspect ratio will scale result + // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given + // gradient vector defined by Start,End + const basegfx::B2DVector aVector(getEnd() - getStart()); + const double fVectorLength(aVector.getLength()); + + aUnitGradientToObject.scale(fVectorLength, 1.0); + aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); + aUnitGradientToObject.translate(getStart().getX(), getStart().getY()); + + aUnitGradientToObject *= getGradientTransform(); + + // create full transform from unit gradient coordinates to object coordinates + // including the SvgGradient transformation + aUnitGradientToObject *= aObjectTransform; } + else + { + // interpret in object coordinate system -> object aspect ratio will not scale result + const basegfx::B2DPoint aStart(aObjectTransform * getStart()); + const basegfx::B2DPoint aEnd(aObjectTransform * getEnd()); + const basegfx::B2DVector aVector(aEnd - aStart); + + aUnitGradientToObject.scale(aVector.getLength(), 1.0); + aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); + aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); + + aUnitGradientToObject *= getGradientTransform(); + } + + return aUnitGradientToObject; + } + Primitive2DReference SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const + { if(getSingleEntry()) { // fill with last existing color - createSingleGradientEntryFill(rContainer); + return createSingleGradientEntryFill(); } else if(getCreatesContent()) { // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely // invisible, width and height to fill are not empty - const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); - const double fPolyWidth(aPolyRange.getWidth()); - const double fPolyHeight(aPolyRange.getHeight()); - - // create ObjectTransform based on polygon range - const basegfx::B2DHomMatrix aObjectTransform( - basegfx::utils::createScaleTranslateB2DHomMatrix( - fPolyWidth, fPolyHeight, - aPolyRange.getMinX(), aPolyRange.getMinY())); - basegfx::B2DHomMatrix aUnitGradientToObject; - - if(getUseUnitCoordinates()) - { - // interpret in unit coordinate system -> object aspect ratio will scale result - // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given - // gradient vector defined by Start,End - const basegfx::B2DVector aVector(getEnd() - getStart()); - const double fVectorLength(aVector.getLength()); - - aUnitGradientToObject.scale(fVectorLength, 1.0); - aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); - aUnitGradientToObject.translate(getStart().getX(), getStart().getY()); - - aUnitGradientToObject *= getGradientTransform(); - - // create full transform from unit gradient coordinates to object coordinates - // including the SvgGradient transformation - aUnitGradientToObject *= aObjectTransform; - } - else - { - // interpret in object coordinate system -> object aspect ratio will not scale result - const basegfx::B2DPoint aStart(aObjectTransform * getStart()); - const basegfx::B2DPoint aEnd(aObjectTransform * getEnd()); - const basegfx::B2DVector aVector(aEnd - aStart); - - aUnitGradientToObject.scale(aVector.getLength(), 1.0); - aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); - aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); - - aUnitGradientToObject *= getGradientTransform(); - } + basegfx::B2DHomMatrix aUnitGradientToObject(createUnitGradientToObjectTransformation()); // create inverse from it basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); @@ -538,7 +541,7 @@ namespace drawinglayer::primitive2d Primitive2DContainer aTargetColor; Primitive2DContainer aTargetOpacity; - if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0)) + if(aUnitRange.getWidth() > 0.0) { // add a pre-multiply to aUnitGradientToObject to allow // multiplication of the polygon(xl, 0.0, xr, 1.0) @@ -555,22 +558,24 @@ namespace drawinglayer::primitive2d aUnitRange.getMaxX()); } - createResult(rContainer, aTargetColor, aTargetOpacity, aUnitGradientToObject); + return createResult(std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject); } + return nullptr; } SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D( const basegfx::B2DHomMatrix& rGradientTransform, const basegfx::B2DPolyPolygon& rPolyPolygon, - const SvgGradientEntryVector& rGradientEntries, + SvgGradientEntryVector&& rGradientEntries, const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd, bool bUseUnitCoordinates, SpreadMethod aSpreadMethod) - : BufferedDecompositionPrimitive2D(), - SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod), + : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod), maEnd(rEnd) { + // ensure Preconditions are checked + checkPreconditions(); } SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D() @@ -598,7 +603,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D) + sal_uInt32 SvgLinearGradientPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D; + } } // end of namespace drawinglayer::primitive2d @@ -641,8 +649,9 @@ namespace drawinglayer::primitive2d if(isFocalSet()) { - const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); - const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); + const basegfx::B2DVector aFocalVector(getFocal() - getStart()); + const basegfx::B2DVector aTranslateFrom(aFocalVector * (maFocalLength - fScaleFrom)); + const basegfx::B2DVector aTranslateTo(aFocalVector * (maFocalLength - fScaleTo)); rTargetColor.push_back( new SvgRadialAtomPrimitive2D( @@ -666,8 +675,9 @@ namespace drawinglayer::primitive2d if(isFocalSet()) { - const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); - const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); + const basegfx::B2DVector aFocalVector(getFocal() - getStart()); + const basegfx::B2DVector aTranslateFrom(aFocalVector * (maFocalLength - fScaleFrom)); + const basegfx::B2DVector aTranslateTo(aFocalVector * (maFocalLength - fScaleTo)); rTargetOpacity.push_back( new SvgRadialAtomPrimitive2D( @@ -685,62 +695,64 @@ namespace drawinglayer::primitive2d } } - void SvgRadialGradientPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + basegfx::B2DHomMatrix SvgRadialGradientPrimitive2D::createUnitGradientToObjectTransformation() const { - if(!getPreconditionsChecked()) + const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); + const double fPolyWidth(aPolyRange.getWidth()); + const double fPolyHeight(aPolyRange.getHeight()); + + // create ObjectTransform based on polygon range + const basegfx::B2DHomMatrix aObjectTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix( + fPolyWidth, fPolyHeight, + aPolyRange.getMinX(), aPolyRange.getMinY())); + basegfx::B2DHomMatrix aUnitGradientToObject; + + if(getUseUnitCoordinates()) + { + // interpret in unit coordinate system -> object aspect ratio will scale result + // create unit transform from unit vector to given linear gradient vector + aUnitGradientToObject.scale(getRadius(), getRadius()); + aUnitGradientToObject.translate(getStart().getX(), getStart().getY()); + + if(!getGradientTransform().isIdentity()) + { + aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject; + } + + // create full transform from unit gradient coordinates to object coordinates + // including the SvgGradient transformation + aUnitGradientToObject = aObjectTransform * aUnitGradientToObject; + } + else { - const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions(); + // interpret in object coordinate system -> object aspect ratio will not scale result + // use X-Axis with radius, it was already made relative to object width when coming from + // SVG import + const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength()); + const basegfx::B2DPoint aStart(aObjectTransform * getStart()); + + aUnitGradientToObject.scale(fRadius, fRadius); + aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); + + aUnitGradientToObject *= getGradientTransform(); } + return aUnitGradientToObject; + } + + Primitive2DReference SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const + { if(getSingleEntry()) { // fill with last existing color - createSingleGradientEntryFill(rContainer); + return createSingleGradientEntryFill(); } else if(getCreatesContent()) { // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely // invisible, width and height to fill are not empty - const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); - const double fPolyWidth(aPolyRange.getWidth()); - const double fPolyHeight(aPolyRange.getHeight()); - - // create ObjectTransform based on polygon range - const basegfx::B2DHomMatrix aObjectTransform( - basegfx::utils::createScaleTranslateB2DHomMatrix( - fPolyWidth, fPolyHeight, - aPolyRange.getMinX(), aPolyRange.getMinY())); - basegfx::B2DHomMatrix aUnitGradientToObject; - - if(getUseUnitCoordinates()) - { - // interpret in unit coordinate system -> object aspect ratio will scale result - // create unit transform from unit vector to given linear gradient vector - aUnitGradientToObject.scale(getRadius(), getRadius()); - aUnitGradientToObject.translate(getStart().getX(), getStart().getY()); - - if(!getGradientTransform().isIdentity()) - { - aUnitGradientToObject = getGradientTransform() * aUnitGradientToObject; - } - - // create full transform from unit gradient coordinates to object coordinates - // including the SvgGradient transformation - aUnitGradientToObject = aObjectTransform * aUnitGradientToObject; - } - else - { - // interpret in object coordinate system -> object aspect ratio will not scale result - // use X-Axis with radius, it was already made relative to object width when coming from - // SVG import - const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength()); - const basegfx::B2DPoint aStart(aObjectTransform * getStart()); - - aUnitGradientToObject.scale(fRadius, fRadius); - aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); - - aUnitGradientToObject *= getGradientTransform(); - } + basegfx::B2DHomMatrix aUnitGradientToObject(createUnitGradientToObjectTransformation()); // create inverse from it basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); @@ -779,33 +791,32 @@ namespace drawinglayer::primitive2d fMax); } - createResult(rContainer, aTargetColor, aTargetOpacity, aUnitGradientToObject, true); + return createResult(std::move(aTargetColor), std::move(aTargetOpacity), aUnitGradientToObject, true); } + return nullptr; } SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D( const basegfx::B2DHomMatrix& rGradientTransform, const basegfx::B2DPolyPolygon& rPolyPolygon, - const SvgGradientEntryVector& rGradientEntries, + SvgGradientEntryVector&& rGradientEntries, const basegfx::B2DPoint& rStart, double fRadius, bool bUseUnitCoordinates, SpreadMethod aSpreadMethod, const basegfx::B2DPoint* pFocal) - : BufferedDecompositionPrimitive2D(), - SvgGradientHelper(rGradientTransform, rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod), + : SvgGradientHelper(rGradientTransform, rPolyPolygon, std::move(rGradientEntries), rStart, bUseUnitCoordinates, aSpreadMethod), mfRadius(fRadius), maFocal(rStart), - maFocalVector(0.0, 0.0), - maFocalLength(0.0), - mbFocalSet(false) + maFocalLength(0.0) { if(pFocal && !pFocal->equal(getStart())) { maFocal = *pFocal; - maFocalVector = maFocal - getStart(); - mbFocalSet = true; } + + // ensure Preconditions are checked + checkPreconditions(); } SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D() @@ -816,22 +827,22 @@ namespace drawinglayer::primitive2d { const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); - if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) - { - const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive); + if(!pSvgGradientHelper || !SvgGradientHelper::operator==(*pSvgGradientHelper)) + return false; + + const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive); - if(getRadius() == rCompare.getRadius()) + if(getRadius() == rCompare.getRadius()) + { + if(isFocalSet() == rCompare.isFocalSet()) { - if(isFocalSet() == rCompare.isFocalSet()) + if(isFocalSet()) { - if(isFocalSet()) - { - return getFocal() == rCompare.getFocal(); - } - else - { - return true; - } + return getFocal() == rCompare.getFocal(); + } + else + { + return true; } } } @@ -846,7 +857,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D) + sal_uInt32 SvgRadialGradientPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D; + } } // end of namespace drawinglayer::primitive2d @@ -855,12 +869,12 @@ namespace drawinglayer::primitive2d namespace drawinglayer::primitive2d { - void SvgLinearAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { const double fDelta(getOffsetB() - getOffsetA()); if(basegfx::fTools::equalZero(fDelta)) - return; + return nullptr; // use one discrete unit for overlap (one pixel) const double fDiscreteUnit(getDiscreteUnit()); @@ -868,6 +882,12 @@ namespace drawinglayer::primitive2d // use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit)); + // HACK: Splitting a gradient into adjacent polygons with gradually changing color is silly. + // If antialiasing is used to draw them, the AA-ed adjacent edges won't line up perfectly + // because of the AA (see SkiaSalGraphicsImpl::mergePolyPolygonToPrevious()). + // Make the polygons a bit wider, so they the partial overlap "fixes" this. + const double fixup = SkiaHelper::isVCLSkiaEnabled() ? fDiscreteUnit / 2 : 0; + // tdf#117949 Use a small amount of discrete overlap at the edges. Usually this // should be exactly 0.0 and 1.0, but there were cases when this gets clipped // against the mask polygon which got numerically problematic. @@ -881,29 +901,31 @@ namespace drawinglayer::primitive2d basegfx::B2DRange( getOffsetA() - fDiscreteUnit, -0.0001, // TTTT -> should be 0.0, see comment above - getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, + getOffsetA() + (fDelta / nSteps) + fDiscreteUnit + fixup, 1.0001))); // TTTT -> should be 1.0, see comment above // prepare loop (inside to outside, [0.0 .. 1.0[) double fUnitScale(0.0); const double fUnitStep(1.0 / nSteps); + Primitive2DContainer aContainer; + aContainer.resize(nSteps); for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) { basegfx::B2DPolygon aNew(aPolygon); - aNew.transform(basegfx::utils::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0)); - rContainer.push_back(new PolyPolygonColorPrimitive2D( + aNew.translate(fDelta * fUnitScale, 0.0); + aContainer[a] = new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aNew), - basegfx::interpolate(getColorA(), getColorB(), fUnitScale))); + basegfx::interpolate(getColorA(), getColorB(), fUnitScale)); } + return new GroupPrimitive2D(std::move(aContainer)); } SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D( const basegfx::BColor& aColorA, double fOffsetA, const basegfx::BColor& aColorB, double fOffsetB) - : DiscreteMetricDependentPrimitive2D(), - maColorA(aColorA), + : maColorA(aColorA), maColorB(aColorB), mfOffsetA(fOffsetA), mfOffsetB(fOffsetB) @@ -931,7 +953,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D) + sal_uInt32 SvgLinearAtomPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D; + } } // end of namespace drawinglayer::primitive2d @@ -940,12 +965,12 @@ namespace drawinglayer::primitive2d namespace drawinglayer::primitive2d { - void SvgRadialAtomPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { const double fDeltaScale(getScaleB() - getScaleA()); if(basegfx::fTools::equalZero(fDeltaScale)) - return; + return nullptr; // use one discrete unit for overlap (one pixel) const double fDiscreteUnit(getDiscreteUnit()); @@ -957,6 +982,8 @@ namespace drawinglayer::primitive2d double fUnitScale(0.0); const double fUnitStep(1.0 / nSteps); + Primitive2DContainer aContainer; + aContainer.resize(nSteps); for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) { basegfx::B2DHomMatrix aTransform; @@ -986,17 +1013,17 @@ namespace drawinglayer::primitive2d basegfx::B2DPolygon aNew(basegfx::utils::createPolygonFromUnitCircle()); aNew.transform(aTransform); - rContainer.push_back(new PolyPolygonColorPrimitive2D( + aContainer[a] = new PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aNew), - basegfx::interpolate(getColorB(), getColorA(), fUnitScale))); + basegfx::interpolate(getColorB(), getColorA(), fUnitScale)); } + return new GroupPrimitive2D(std::move(aContainer)); } SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA, const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB) - : DiscreteMetricDependentPrimitive2D(), - maColorA(aColorA), + : maColorA(aColorA), maColorB(aColorB), mfScaleA(fScaleA), mfScaleB(fScaleB) @@ -1027,8 +1054,7 @@ namespace drawinglayer::primitive2d SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( const basegfx::BColor& aColorA, double fScaleA, const basegfx::BColor& aColorB, double fScaleB) - : DiscreteMetricDependentPrimitive2D(), - maColorA(aColorA), + : maColorA(aColorA), maColorB(aColorB), mfScaleA(fScaleA), mfScaleB(fScaleB) @@ -1051,24 +1077,24 @@ namespace drawinglayer::primitive2d bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) - { - const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive); + if(!DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + return false; - if(getColorA() == rCompare.getColorA() - && getColorB() == rCompare.getColorB() - && getScaleA() == rCompare.getScaleA() - && getScaleB() == rCompare.getScaleB()) + const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive); + + if(getColorA() == rCompare.getColorA() + && getColorB() == rCompare.getColorB() + && getScaleA() == rCompare.getScaleA() + && getScaleB() == rCompare.getScaleB()) + { + if(isTranslateSet() && rCompare.isTranslateSet()) { - if(isTranslateSet() && rCompare.isTranslateSet()) - { - return (getTranslateA() == rCompare.getTranslateA() - && getTranslateB() == rCompare.getTranslateB()); - } - else if(!isTranslateSet() && !rCompare.isTranslateSet()) - { - return true; - } + return (getTranslateA() == rCompare.getTranslateA() + && getTranslateB() == rCompare.getTranslateB()); + } + else if(!isTranslateSet() && !rCompare.isTranslateSet()) + { + return true; } } @@ -1076,7 +1102,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D) + sal_uInt32 SvgRadialAtomPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/textbreakuphelper.cxx b/drawinglayer/source/primitive2d/textbreakuphelper.cxx index df37aa01356e..eac53589a7ff 100644 --- a/drawinglayer/source/primitive2d/textbreakuphelper.cxx +++ b/drawinglayer/source/primitive2d/textbreakuphelper.cxx @@ -30,9 +30,6 @@ namespace drawinglayer::primitive2d { TextBreakupHelper::TextBreakupHelper(const TextSimplePortionPrimitive2D& rSource) : mrSource(rSource), - mxResult(), - maTextLayouter(), - maDecTrans(), mbNoDXArray(false) { maDecTrans = mrSource.getTextTransform(); @@ -61,6 +58,7 @@ namespace drawinglayer::primitive2d // prepare values for new portion basegfx::B2DHomMatrix aNewTransform; std::vector< double > aNewDXArray; + std::vector< sal_Bool > aNewKashidaArray; const bool bNewStartIsNotOldStart(nIndex > mrSource.getTextPosition()); if(!mbNoDXArray) @@ -71,6 +69,13 @@ namespace drawinglayer::primitive2d mrSource.getDXArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); } + if(!mbNoDXArray && !mrSource.getKashidaArray().empty()) + { + aNewKashidaArray = std::vector< sal_Bool >( + mrSource.getKashidaArray().begin() + (nIndex - mrSource.getTextPosition()), + mrSource.getKashidaArray().begin() + ((nIndex + nLength) - mrSource.getTextPosition())); + } + if(bNewStartIsNotOldStart) { // needs to be moved to a new start position @@ -107,17 +112,14 @@ namespace drawinglayer::primitive2d { // DXArray values need to be corrected with the offset, too. Here, // take the scaled offset since the DXArray is scaled - const sal_uInt32 nArraySize(aNewDXArray.size()); - - for(sal_uInt32 a(0); a < nArraySize; a++) + for(double &rNewDX: aNewDXArray) { - aNewDXArray[a] -= fOffset; + rNewDX -= fOffset; } } } // add text transformation to new transformation - // coverity[swapped_arguments : FALSE] - this is in the correct order aNewTransform *= maDecTrans.getB2DHomMatrix(); // callback to allow evtl. changes @@ -139,7 +141,8 @@ namespace drawinglayer::primitive2d mrSource.getText(), nIndex, nLength, - aNewDXArray, + std::move(aNewDXArray), + std::move(aNewKashidaArray), mrSource.getFontAttribute(), mrSource.getLocale(), mrSource.getFontColor(), @@ -170,7 +173,8 @@ namespace drawinglayer::primitive2d mrSource.getText(), nIndex, nLength, - aNewDXArray, + std::move(aNewDXArray), + std::move(aNewKashidaArray), mrSource.getFontAttribute(), mrSource.getLocale(), mrSource.getFontColor())); @@ -192,7 +196,7 @@ namespace drawinglayer::primitive2d if(!xBreakIterator.is()) { - css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + const css::uno::Reference< css::uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() ); xBreakIterator = css::i18n::BreakIterator::create(xContext); } @@ -261,17 +265,17 @@ namespace drawinglayer::primitive2d } } - mxResult = aTempResult; + mxResult = std::move(aTempResult); } - const Primitive2DContainer& TextBreakupHelper::getResult(BreakupUnit aBreakupUnit) const + Primitive2DContainer TextBreakupHelper::extractResult(BreakupUnit aBreakupUnit) { if(mxResult.empty()) { - const_cast< TextBreakupHelper* >(this)->breakup(aBreakupUnit); + breakup(aBreakupUnit); } - return mxResult; + return std::move(mxResult); } } // end of namespace diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx index afc841fcf3bd..bfa5ebbb7eea 100644 --- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx @@ -22,52 +22,81 @@ #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <primitive2d/texteffectprimitive2d.hxx> #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> #include <primitive2d/textlineprimitive2d.hxx> #include <primitive2d/textstrikeoutprimitive2d.hxx> #include <drawinglayer/primitive2d/textbreakuphelper.hxx> - +#include <vcl/vcllayout.hxx> namespace drawinglayer::primitive2d { void TextDecoratedPortionPrimitive2D::impCreateGeometryContent( - std::vector< Primitive2DReference >& rTarget, + Primitive2DContainer& rTarget, basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans, const OUString& rText, sal_Int32 nTextPosition, sal_Int32 nTextLength, const std::vector< double >& rDXArray, + const std::vector< sal_Bool >& rKashidaArray, const attribute::FontAttribute& rFontAttribute) const { // create the SimpleTextPrimitive needed in any case - rTarget.push_back(Primitive2DReference( + rTarget.push_back( new TextSimplePortionPrimitive2D( rDecTrans.getB2DHomMatrix(), rText, nTextPosition, nTextLength, - rDXArray, + std::vector(rDXArray), + std::vector(rKashidaArray), rFontAttribute, getLocale(), - getFontColor()))); + getFontColor())); + + // create and add decoration + const Primitive2DContainer& rDecorationGeometryContent( + getOrCreateDecorationGeometryContent( + rDecTrans, + rText, + nTextPosition, + nTextLength, + rDXArray)); + + rTarget.insert(rTarget.end(), rDecorationGeometryContent.begin(), rDecorationGeometryContent.end()); + } + const Primitive2DContainer& TextDecoratedPortionPrimitive2D::getOrCreateDecorationGeometryContent( + basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans, + const OUString& rText, + sal_Int32 nTextPosition, + sal_Int32 nTextLength, + const std::vector< double >& rDXArray) const + { // see if something else needs to be done const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline()); const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline()); const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout()); + const bool bEmphasisMarkUsed(TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark() + && (getEmphasisMarkAbove() || getEmphasisMarkBelow())); - if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)) - return; + if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed || bEmphasisMarkUsed)) + { + // not used, return empty Primitive2DContainer + return maBufferedDecorationGeometry; + } - // common preparations - TextLayouterDevice aTextLayouter; + if (!maBufferedDecorationGeometry.empty()) + { + // if not empty it is used -> append and return Primitive2DContainer + return maBufferedDecorationGeometry; + } - // TextLayouterDevice is needed to get metrics for text decorations like - // underline/strikeout/emphasis marks from it. For setup, the font size is needed - aTextLayouter.setFontAttribute( - getFontAttribute(), - rDecTrans.getScale().getX(), - rDecTrans.getScale().getY(), - getLocale()); + // common preparations - create TextLayouterDevice + primitive2d::TextLayouterDevice aTextLayouter; + createTextLayouter(aTextLayouter); // get text width double fTextWidth(0.0); @@ -91,86 +120,209 @@ namespace drawinglayer::primitive2d if(bOverlineUsed) { + // for Relief we have to manipulate the OverlineColor + basegfx::BColor aOverlineColor(getOverlineColor()); + if (hasTextRelief() && COL_BLACK.getBColor() == aOverlineColor) + aOverlineColor = COL_WHITE.getBColor(); + // create primitive geometry for overline - rTarget.push_back(Primitive2DReference( + maBufferedDecorationGeometry.push_back( new TextLinePrimitive2D( rDecTrans.getB2DHomMatrix(), fTextWidth, aTextLayouter.getOverlineOffset(), aTextLayouter.getOverlineHeight(), getFontOverline(), - getOverlineColor()))); + aOverlineColor)); } if(bUnderlineUsed) { + // for Relief we have to manipulate the TextlineColor + basegfx::BColor aTextlineColor(getTextlineColor()); + if (hasTextRelief() && COL_BLACK.getBColor() == aTextlineColor) + aTextlineColor = COL_WHITE.getBColor(); + // create primitive geometry for underline - rTarget.push_back(Primitive2DReference( + maBufferedDecorationGeometry.push_back( new TextLinePrimitive2D( rDecTrans.getB2DHomMatrix(), fTextWidth, aTextLayouter.getUnderlineOffset(), aTextLayouter.getUnderlineHeight(), getFontUnderline(), - getTextlineColor()))); + aTextlineColor)); } - if(!bStrikeoutUsed) - return; - - // create primitive geometry for strikeout - if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout()) + if(bStrikeoutUsed) { - // strikeout with character - const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X'); + // for Relief we have to manipulate the FontColor + basegfx::BColor aFontColor(getFontColor()); + if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor) + aFontColor = COL_WHITE.getBColor(); - rTarget.push_back(Primitive2DReference( - new TextCharacterStrikeoutPrimitive2D( - rDecTrans.getB2DHomMatrix(), - fTextWidth, - getFontColor(), - aStrikeoutChar, - getFontAttribute(), - getLocale()))); + // create primitive geometry for strikeout + if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout()) + { + // strikeout with character + const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X'); + + maBufferedDecorationGeometry.push_back( + new TextCharacterStrikeoutPrimitive2D( + rDecTrans.getB2DHomMatrix(), + fTextWidth, + aFontColor, + aStrikeoutChar, + getFontAttribute(), + getLocale())); + } + else + { + // strikeout with geometry + maBufferedDecorationGeometry.push_back( + new TextGeometryStrikeoutPrimitive2D( + rDecTrans.getB2DHomMatrix(), + fTextWidth, + aFontColor, + aTextLayouter.getUnderlineHeight(), + aTextLayouter.getStrikeoutOffset(), + getTextStrikeout())); + } } - else + + if (bEmphasisMarkUsed) { - // strikeout with geometry - rTarget.push_back(Primitive2DReference( - new TextGeometryStrikeoutPrimitive2D( - rDecTrans.getB2DHomMatrix(), - fTextWidth, - getFontColor(), - aTextLayouter.getUnderlineHeight(), - aTextLayouter.getStrikeoutOffset(), - getTextStrikeout()))); + // create primitives for EmphasisMark visualization - we need a SalLayout + std::unique_ptr<SalLayout> pSalLayout(createSalLayout(aTextLayouter)); + + if (pSalLayout) + { + // for Relief we have to manipulate the FontColor + basegfx::BColor aFontColor(getFontColor()); + if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor) + aFontColor = COL_WHITE.getBColor(); + + // placeholders for repeated content, only created once + Primitive2DReference aShape; + Primitive2DReference aRect1; + Primitive2DReference aRect2; + + // space to collect primitives for EmphasisMark + Primitive2DContainer aEmphasisContent; + + // callback collector will produce geometry already scaled, so + // prepare local transform without FontScale + const basegfx::B2DHomMatrix aObjTransformWithoutScale( + basegfx::utils::createShearXRotateTranslateB2DHomMatrix( + rDecTrans.getShearX(), rDecTrans.getRotate(), rDecTrans.getTranslate())); + + // the callback from OutputDevice::createEmphasisMarks providing the data + // for each EmphasisMark + auto aEmphasisCallback([&aShape, &aRect1, &aRect2, &aEmphasisContent, &aObjTransformWithoutScale, &aFontColor]( + const basegfx::B2DPoint& rOutPoint, const basegfx::B2DPolyPolygon& rShape, + bool isPolyLine, const tools::Rectangle& rRect1, const tools::Rectangle& rRect2) + { + // prepare complete ObjectTransform + const basegfx::B2DHomMatrix aTransform( + aObjTransformWithoutScale * basegfx::utils::createTranslateB2DHomMatrix(rOutPoint)); + + if (rShape.count()) + { + // create PolyPolygon if provided + if (!aShape) + { + if (isPolyLine) + aShape = new PolyPolygonHairlinePrimitive2D(rShape, aFontColor); + else + aShape = new PolyPolygonColorPrimitive2D(rShape, aFontColor); + } + + aEmphasisContent.push_back( + new TransformPrimitive2D( + aTransform, + Primitive2DContainer { aShape } )); + } + + if (!rRect1.IsEmpty()) + { + // create Rectangle1 if provided + if (!aRect1) + aRect1 = new FilledRectanglePrimitive2D( + basegfx::B2DRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom()), aFontColor); + + aEmphasisContent.push_back( + new TransformPrimitive2D( + aTransform, + Primitive2DContainer { aRect1 } )); + } + + if (!rRect2.IsEmpty()) + { + // create Rectangle2 if provided + if (!aRect2) + aRect2 = new FilledRectanglePrimitive2D( + basegfx::B2DRange(rRect2.Left(), rRect2.Top(), rRect2.Right(), rRect2.Bottom()), aFontColor); + + aEmphasisContent.push_back( + new TransformPrimitive2D( + aTransform, + Primitive2DContainer { aRect2 } )); + } + }); + + // call tooling method in vcl to generate the graphic representations + aTextLayouter.createEmphasisMarks( + *pSalLayout, + getTextEmphasisMark(), + getEmphasisMarkAbove(), + aEmphasisCallback); + + if (!aEmphasisContent.empty()) + { + // if we got graphic representations of EmphasisMark, add + // them to BufferedDecorationGeometry. Also embed them to + // a TextHierarchyEmphasisMarkPrimitive2D GroupPrimitive + // to be able to evtl. handle these in a special way + maBufferedDecorationGeometry.push_back( + new TextHierarchyEmphasisMarkPrimitive2D(std::move(aEmphasisContent))); + } + } } - // TODO: Handle Font Emphasis Above/Below + // append local result and return + return maBufferedDecorationGeometry; } - void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + const Primitive2DContainer& TextDecoratedPortionPrimitive2D::getOrCreateBrokenUpText() const { - if(getWordLineMode()) + if(!getWordLineMode()) { - // support for single word mode; split to single word primitives - // using TextBreakupHelper - const TextBreakupHelper aTextBreakupHelper(*this); - const Primitive2DContainer& aBroken(aTextBreakupHelper.getResult(BreakupUnit::Word)); + // return empty Primitive2DContainer + return maBufferedBrokenUpText; + } - if(!aBroken.empty()) - { - // was indeed split to several words, use as result - rContainer.insert(rContainer.end(), aBroken.begin(), aBroken.end()); - return; - } - else - { - // no split, was already a single word. Continue to - // decompose local entity - } + if (!maBufferedBrokenUpText.empty()) + { + // if not empty it is used -> return Primitive2DContainer + return maBufferedBrokenUpText; + } + + // support for single word mode; split to single word primitives + // using TextBreakupHelper + TextBreakupHelper aTextBreakupHelper(*this); + maBufferedBrokenUpText = aTextBreakupHelper.extractResult(BreakupUnit::Word); + return maBufferedBrokenUpText; + } + + Primitive2DReference TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const + { + if (!getOrCreateBrokenUpText().empty()) + { + // if BrokenUpText/WordLineMode is used, go into recursion + Primitive2DContainer aContent(getOrCreateBrokenUpText()); + return new GroupPrimitive2D(std::move(aContent)); } - std::vector< Primitive2DReference > aNewPrimitives; + basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform()); Primitive2DContainer aRetval; @@ -190,115 +342,113 @@ namespace drawinglayer::primitive2d getFontAttribute().getBiDiStrong()); // handle as one word - impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute); + impCreateGeometryContent(aRetval, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), getKashidaArray(), aNewFontAttribute); - // convert to Primitive2DSequence - const sal_uInt32 nMemberCount(aNewPrimitives.size()); + // Handle Shadow, Outline and TextRelief + if(aRetval.empty()) + return nullptr; - if(nMemberCount) + if(hasShadow() || hasTextRelief() || hasOutline()) { - aRetval.resize(nMemberCount); + Primitive2DReference aShadow; - for(sal_uInt32 a(0); a < nMemberCount; a++) + if(hasShadow()) { - aRetval[a] = aNewPrimitives[a]; + // create shadow with current content (in aRetval). Text shadow + // is constant, relative to font size, rotated with the text and has a + // constant color. + // shadow parameter values + static const double fFactor(1.0 / 24.0); + const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); + + // see OutputDevice::ImplDrawSpecialText -> no longer simple fixed color + const basegfx::BColor aBlack(0.0, 0.0, 0.0); + basegfx::BColor aShadowColor(aBlack); + if (aBlack == getFontColor() || getFontColor().luminance() < (8.0 / 255.0)) + aShadowColor = COL_LIGHTGRAY.getBColor(); + + // prepare shadow transform matrix + const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix( + fTextShadowOffset, fTextShadowOffset)); + + // create shadow primitive + aShadow = new ShadowPrimitive2D( + aShadowTransform, + aShadowColor, + 0, // fShadowBlur = 0, there's no blur for text shadow yet. + Primitive2DContainer(aRetval)); } - } - // Handle Shadow, Outline and TextRelief - if(!aRetval.empty()) - { - // outline AND shadow depend on NO TextRelief (see dialog) - const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief()); - const bool bHasShadow(!bHasTextRelief && getShadow()); - const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline()); - - if(bHasShadow || bHasTextRelief || bHasOutline) + if(hasTextRelief()) { - Primitive2DReference aShadow; + // create emboss using an own helper primitive since this will + // be view-dependent + const basegfx::BColor aBBlack(0.0, 0.0, 0.0); + const bool bDefaultTextColor(aBBlack == getFontColor()); + TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed); - if(bHasShadow) + if(bDefaultTextColor) { - // create shadow with current content (in aRetval). Text shadow - // is constant, relative to font size, rotated with the text and has a - // constant color. - // shadow parameter values - static const double fFactor(1.0 / 24.0); - const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); - static basegfx::BColor aShadowColor(0.3, 0.3, 0.3); - - // prepare shadow transform matrix - const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix( - fTextShadowOffset, fTextShadowOffset)); - - // create shadow primitive - aShadow = new ShadowPrimitive2D( - aShadowTransform, - aShadowColor, - 0, // fShadowBlur = 0, there's no blur for text shadow yet. - aRetval); - } - - if(bHasTextRelief) - { - // create emboss using an own helper primitive since this will - // be view-dependent - const basegfx::BColor aBBlack(0.0, 0.0, 0.0); - const bool bDefaultTextColor(aBBlack == getFontColor()); - TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed); - - if(bDefaultTextColor) + if(TEXT_RELIEF_ENGRAVED == getTextRelief()) { - if(TEXT_RELIEF_ENGRAVED == getTextRelief()) - { - aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault; - } - else - { - aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault; - } + aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault; } else { - if(TEXT_RELIEF_ENGRAVED == getTextRelief()) - { - aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved; - } - else - { - aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed; - } + aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault; } - Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( - aRetval, - aDecTrans.getTranslate(), - aDecTrans.getRotate(), - aTextEffectStyle2D)); - aRetval = Primitive2DContainer { aNewTextEffect }; + aRetval = Primitive2DContainer { + new TextEffectPrimitive2D( + std::move(aRetval), + aDecTrans.getTranslate(), + aDecTrans.getRotate(), + aTextEffectStyle2D) + }; } - else if(bHasOutline) + else { // create outline using an own helper primitive since this will // be view-dependent - Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( - aRetval, + aRetval = Primitive2DContainer { + new TextEffectPrimitive2D( + std::move(aRetval), + aDecTrans.getTranslate(), + aDecTrans.getRotate(), + TextEffectStyle2D::Outline) + }; + } + + aRetval = Primitive2DContainer { + Primitive2DReference(new TextEffectPrimitive2D( + std::move(aRetval), aDecTrans.getTranslate(), aDecTrans.getRotate(), - TextEffectStyle2D::Outline)); - aRetval = Primitive2DContainer { aNewTextEffect }; - } + aTextEffectStyle2D)) + }; + } + else if(hasOutline()) + { + // create outline using an own helper primitive since this will + // be view-dependent + aRetval = Primitive2DContainer { + Primitive2DReference(new TextEffectPrimitive2D( + std::move(aRetval), + aDecTrans.getTranslate(), + aDecTrans.getRotate(), + TextEffectStyle2D::Outline)) + }; + } - if(aShadow.is()) - { - // put shadow in front if there is one to paint timely before - // but placed behind content - aRetval.insert(aRetval.begin(), aShadow); - } + if(aShadow.is()) + { + // put shadow in front if there is one to paint timely before + // but placed behind content + aRetval.insert(aRetval.begin(), aShadow); } } - rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); + return new GroupPrimitive2D(std::move(aRetval)); } TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D( @@ -307,7 +457,8 @@ namespace drawinglayer::primitive2d const OUString& rText, sal_Int32 nTextPosition, sal_Int32 nTextLength, - const std::vector< double >& rDXArray, + std::vector< double >&& rDXArray, + std::vector< sal_Bool >&& rKashidaArray, const attribute::FontAttribute& rFontAttribute, const css::lang::Locale& rLocale, const basegfx::BColor& rFontColor, @@ -326,7 +477,19 @@ namespace drawinglayer::primitive2d bool bEmphasisMarkBelow, TextRelief eTextRelief, bool bShadow) - : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, rDXArray, rFontAttribute, rLocale, rFontColor, false, 0, rFillColor), + : TextSimplePortionPrimitive2D( + rNewTransform, + rText, + nTextPosition, + nTextLength, + std::move(rDXArray), + std::move(rKashidaArray), + rFontAttribute, + rLocale, + rFontColor, + rFillColor), + maBufferedBrokenUpText(), + maBufferedDecorationGeometry(), maOverlineColor(rOverlineColor), maTextlineColor(rTextlineColor), meFontOverline(eFontOverline), @@ -342,6 +505,25 @@ namespace drawinglayer::primitive2d { } + bool TextDecoratedPortionPrimitive2D::hasTextRelief() const + { + return TEXT_RELIEF_NONE != getTextRelief(); + } + + bool TextDecoratedPortionPrimitive2D::hasShadow() const + { + // not allowed with TextRelief, else defined in FontAttributes + return !hasTextRelief() && getShadow(); + } + + bool TextDecoratedPortionPrimitive2D::hasTextDecoration() const + { + return TEXT_LINE_NONE != getFontOverline() + || TEXT_LINE_NONE != getFontUnderline() + || TEXT_STRIKEOUT_NONE != getTextStrikeout() + || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark(); + } + bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { if(TextSimplePortionPrimitive2D::operator==(rPrimitive)) @@ -392,7 +574,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) + sal_uInt32 TextDecoratedPortionPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx index abfe28e3ecca..f1bb6812bfd2 100644 --- a/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/texteffectprimitive2d.cxx @@ -24,222 +24,221 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> - namespace drawinglayer::primitive2d { - const double fDiscreteSize(1.1); +const double fDiscreteSize(1.1); - void TextEffectPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const +Primitive2DReference TextEffectPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& rViewInformation) const +{ + // get the distance of one discrete units from target display. Use between 1.0 and sqrt(2) to + // have good results on rotated objects, too + const basegfx::B2DVector aDistance(rViewInformation.getInverseObjectToViewTransformation() + * basegfx::B2DVector(fDiscreteSize, fDiscreteSize)); + const basegfx::B2DVector aDiagonalDistance(aDistance * (1.0 / 1.44)); + + Primitive2DContainer aContainer; + switch (getTextEffectStyle2D()) + { + case TextEffectStyle2D::ReliefEmbossed: + case TextEffectStyle2D::ReliefEmbossedDefault: + case TextEffectStyle2D::ReliefEngravedDefault: { - // get the distance of one discrete units from target display. Use between 1.0 and sqrt(2) to - // have good results on rotated objects, too - const basegfx::B2DVector aDistance(rViewInformation.getInverseObjectToViewTransformation() * - basegfx::B2DVector(fDiscreteSize, fDiscreteSize)); - const basegfx::B2DVector aDiagonalDistance(aDistance * (1.0 / 1.44)); - - switch(getTextEffectStyle2D()) + // prepare transform of sub-group back to (0,0) and align to X-Axis + basegfx::B2DHomMatrix aBackTransform(basegfx::utils::createTranslateB2DHomMatrix( + -getRotationCenter().getX(), -getRotationCenter().getY())); + aBackTransform.rotate(-getDirection()); + + // prepare transform of sub-group back to its position and rotation + basegfx::B2DHomMatrix aForwardTransform( + basegfx::utils::createRotateB2DHomMatrix(getDirection())); + aForwardTransform.translate(getRotationCenter().getX(), getRotationCenter().getY()); + + // create transformation for one discrete unit + const bool bEmbossed(TextEffectStyle2D::ReliefEmbossed == getTextEffectStyle2D() + || TextEffectStyle2D::ReliefEmbossedDefault + == getTextEffectStyle2D()); + const bool bDefaultTextColor( + TextEffectStyle2D::ReliefEmbossedDefault == getTextEffectStyle2D() + || TextEffectStyle2D::ReliefEngravedDefault == getTextEffectStyle2D()); + basegfx::B2DHomMatrix aTransform(aBackTransform); + + if (bEmbossed) { - case TextEffectStyle2D::ReliefEmbossed: - case TextEffectStyle2D::ReliefEngraved: - case TextEffectStyle2D::ReliefEmbossedDefault: - case TextEffectStyle2D::ReliefEngravedDefault: - { - // prepare transform of sub-group back to (0,0) and align to X-Axis - basegfx::B2DHomMatrix aBackTransform(basegfx::utils::createTranslateB2DHomMatrix( - -getRotationCenter().getX(), -getRotationCenter().getY())); - aBackTransform.rotate(-getDirection()); - - // prepare transform of sub-group back to its position and rotation - basegfx::B2DHomMatrix aForwardTransform(basegfx::utils::createRotateB2DHomMatrix(getDirection())); - aForwardTransform.translate(getRotationCenter().getX(), getRotationCenter().getY()); - - // create transformation for one discrete unit - const bool bEmbossed( - TextEffectStyle2D::ReliefEmbossed == getTextEffectStyle2D() - || TextEffectStyle2D::ReliefEmbossedDefault == getTextEffectStyle2D()); - const bool bDefaultTextColor( - TextEffectStyle2D::ReliefEmbossedDefault == getTextEffectStyle2D() - || TextEffectStyle2D::ReliefEngravedDefault == getTextEffectStyle2D()); - basegfx::B2DHomMatrix aTransform(aBackTransform); - - if(bEmbossed) - { - // to bottom-right - aTransform.translate(aDiagonalDistance.getX(), aDiagonalDistance.getY()); - } - else - { - // to top-left - aTransform.translate(-aDiagonalDistance.getX(), -aDiagonalDistance.getY()); - } - - aTransform *= aForwardTransform; - - if(bDefaultTextColor) - { - // emboss/engrave in black, original forced to white - const basegfx::BColorModifierSharedPtr aBColorModifierToGray = - std::make_shared<basegfx::BColorModifier_replace>( - basegfx::BColor(0.0)); - const Primitive2DReference xModifiedColor( - new ModifiedColorPrimitive2D( - getTextContent(), - aBColorModifierToGray)); - - rContainer.push_back( - new TransformPrimitive2D( - aTransform, - Primitive2DContainer { xModifiedColor })); - - // add original, too - const basegfx::BColorModifierSharedPtr aBColorModifierToWhite = - std::make_shared<basegfx::BColorModifier_replace>( - basegfx::BColor(1.0)); - - rContainer.push_back( - new ModifiedColorPrimitive2D( - getTextContent(), - aBColorModifierToWhite)); - } - else - { - // emboss/engrave in gray, keep original's color - const basegfx::BColorModifierSharedPtr aBColorModifierToGray = - std::make_shared<basegfx::BColorModifier_replace>( - basegfx::BColor(0.75)); // 192 - const Primitive2DReference xModifiedColor( - new ModifiedColorPrimitive2D( - getTextContent(), - aBColorModifierToGray)); - - rContainer.push_back( - new TransformPrimitive2D( - aTransform, - Primitive2DContainer { xModifiedColor })); - - // add original, too - rContainer.push_back(new GroupPrimitive2D(getTextContent())); - } - - break; - } - case TextEffectStyle2D::Outline: - { - // create transform primitives in all directions - basegfx::B2DHomMatrix aTransform; - - aTransform.set(0, 2, aDistance.getX()); - aTransform.set(1, 2, 0.0); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, aDiagonalDistance.getX()); - aTransform.set(1, 2, aDiagonalDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, 0.0); - aTransform.set(1, 2, aDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, -aDiagonalDistance.getX()); - aTransform.set(1, 2, aDiagonalDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, -aDistance.getX()); - aTransform.set(1, 2, 0.0); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, -aDiagonalDistance.getX()); - aTransform.set(1, 2, -aDiagonalDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, 0.0); - aTransform.set(1, 2, -aDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - aTransform.set(0, 2, aDiagonalDistance.getX()); - aTransform.set(1, 2, -aDiagonalDistance.getY()); - rContainer.push_back(new TransformPrimitive2D(aTransform, getTextContent())); - - // at last, place original over it, but force to white - const basegfx::BColorModifierSharedPtr aBColorModifierToWhite = - std::make_shared<basegfx::BColorModifier_replace>( - basegfx::BColor(1.0, 1.0, 1.0)); - rContainer.push_back( - new ModifiedColorPrimitive2D( - getTextContent(), - aBColorModifierToWhite)); - - break; - } + // to bottom-right + aTransform.translate(aDiagonalDistance.getX(), aDiagonalDistance.getY()); + } + else + { + // to top-left + aTransform.translate(-aDiagonalDistance.getX(), -aDiagonalDistance.getY()); } - } - TextEffectPrimitive2D::TextEffectPrimitive2D( - const Primitive2DContainer& rTextContent, - const basegfx::B2DPoint& rRotationCenter, - double fDirection, - TextEffectStyle2D eTextEffectStyle2D) - : BufferedDecompositionPrimitive2D(), - maTextContent(rTextContent), - maRotationCenter(rRotationCenter), - mfDirection(fDirection), - meTextEffectStyle2D(eTextEffectStyle2D) - { - } + aTransform *= aForwardTransform; - bool TextEffectPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BasePrimitive2D::operator==(rPrimitive)) + if (bDefaultTextColor) { - const TextEffectPrimitive2D& rCompare = static_cast<const TextEffectPrimitive2D&>(rPrimitive); + // emboss/engrave in black, original forced to white + basegfx::BColorModifierSharedPtr aBColorModifierToGray + = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0)); + Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D( + Primitive2DContainer(getTextContent()), std::move(aBColorModifierToGray))); + + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor })); - return (getTextContent() == rCompare.getTextContent() - && getRotationCenter() == rCompare.getRotationCenter() - && getDirection() == rCompare.getDirection() - && getTextEffectStyle2D() == rCompare.getTextEffectStyle2D()); + // add original, too + basegfx::BColorModifierSharedPtr aBColorModifierToWhite + = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0)); + + aContainer.push_back(new ModifiedColorPrimitive2D( + Primitive2DContainer(getTextContent()), std::move(aBColorModifierToWhite))); + } + else + { + // emboss/engrave in gray, keep original's color + basegfx::BColorModifierSharedPtr aBColorModifierToGray + = std::make_shared<basegfx::BColorModifier_replace>( + basegfx::BColor(0.75)); // 192 + Primitive2DReference xModifiedColor(new ModifiedColorPrimitive2D( + Primitive2DContainer(getTextContent()), std::move(aBColorModifierToGray))); + + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer{ xModifiedColor })); + + // add original, too + aContainer.push_back(new GroupPrimitive2D(Primitive2DContainer(getTextContent()))); } - return false; + break; } - - basegfx::B2DRange TextEffectPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const + case TextEffectStyle2D::Outline: { - // get range of content and grow by used fDiscreteSize. That way it is not necessary to ask - // the whole decomposition for its ranges (which may be expensive with outline mode which - // then will ask 9 times at nearly the same content. This may even be refined here using the - // TextEffectStyle information, e.g. for TEXTEFFECTSTYLE2D_RELIEF the grow needs only to - // be in two directions - basegfx::B2DRange aRetval(getTextContent().getB2DRange(rViewInformation)); - aRetval.grow(fDiscreteSize); - - return aRetval; + // create transform primitives in all directions + basegfx::B2DHomMatrix aTransform; + + aTransform.set(0, 2, aDistance.getX()); + aTransform.set(1, 2, 0.0); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, aDiagonalDistance.getX()); + aTransform.set(1, 2, aDiagonalDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, 0.0); + aTransform.set(1, 2, aDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, -aDiagonalDistance.getX()); + aTransform.set(1, 2, aDiagonalDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, -aDistance.getX()); + aTransform.set(1, 2, 0.0); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, -aDiagonalDistance.getX()); + aTransform.set(1, 2, -aDiagonalDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, 0.0); + aTransform.set(1, 2, -aDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + aTransform.set(0, 2, aDiagonalDistance.getX()); + aTransform.set(1, 2, -aDiagonalDistance.getY()); + aContainer.push_back( + new TransformPrimitive2D(aTransform, Primitive2DContainer(getTextContent()))); + + // at last, place original over it, but force to white + basegfx::BColorModifierSharedPtr aBColorModifierToWhite + = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1.0, 1.0, 1.0)); + aContainer.push_back(new ModifiedColorPrimitive2D( + Primitive2DContainer(getTextContent()), std::move(aBColorModifierToWhite))); + + break; } + } + return new GroupPrimitive2D(std::move(aContainer)); +} + +TextEffectPrimitive2D::TextEffectPrimitive2D(Primitive2DContainer&& rTextContent, + const basegfx::B2DPoint& rRotationCenter, + double fDirection, + TextEffectStyle2D eTextEffectStyle2D) + : maTextContent(std::move(rTextContent)) + , maRotationCenter(rRotationCenter) + , mfDirection(fDirection) + , meTextEffectStyle2D(eTextEffectStyle2D) +{ +} - void TextEffectPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const +bool TextEffectPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BasePrimitive2D::operator==(rPrimitive)) + { + const TextEffectPrimitive2D& rCompare + = static_cast<const TextEffectPrimitive2D&>(rPrimitive); + + return (getTextContent() == rCompare.getTextContent() + && getRotationCenter() == rCompare.getRotationCenter() + && getDirection() == rCompare.getDirection() + && getTextEffectStyle2D() == rCompare.getTextEffectStyle2D()); + } + + return false; +} + +basegfx::B2DRange +TextEffectPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const +{ + // get range of content and grow by used fDiscreteSize. That way it is not necessary to ask + // the whole decomposition for its ranges (which may be expensive with outline mode which + // then will ask 9 times at nearly the same content. This may even be refined here using the + // TextEffectStyle information, e.g. for TEXTEFFECTSTYLE2D_RELIEF the grow needs only to + // be in two directions + basegfx::B2DRange aRetval(getTextContent().getB2DRange(rViewInformation)); + aRetval.grow(fDiscreteSize); + + return aRetval; +} + +void TextEffectPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + if (hasBuffered2DDecomposition()) + { + if (maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation()) { - ::osl::MutexGuard aGuard( m_aMutex ); - - if(!getBuffered2DDecomposition().empty()) - { - if(maLastObjectToViewTransformation != rViewInformation.getObjectToViewTransformation()) - { - // conditions of last local decomposition have changed, delete - const_cast< TextEffectPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); - } - } + // conditions of last local decomposition have changed, delete + const_cast<TextEffectPrimitive2D*>(this)->setBuffered2DDecomposition(nullptr); + } + } - if(getBuffered2DDecomposition().empty()) - { - // remember ViewRange and ViewTransformation - const_cast< TextEffectPrimitive2D* >(this)->maLastObjectToViewTransformation = rViewInformation.getObjectToViewTransformation(); - } + if (!hasBuffered2DDecomposition()) + { + // remember ViewRange and ViewTransformation + const_cast<TextEffectPrimitive2D*>(this)->maLastObjectToViewTransformation + = rViewInformation.getObjectToViewTransformation(); + } - // use parent implementation - BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); - } + // use parent implementation + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} - // provide unique ID - ImplPrimitive2DIDBlock(TextEffectPrimitive2D, PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D) +// provide unique ID +sal_uInt32 TextEffectPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_TEXTEFFECTPRIMITIVE2D; +} } // end of namespace diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx index 77f7f659739c..d0a69ad9a336 100644 --- a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx @@ -19,6 +19,7 @@ #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <comphelper/lok.hxx> using namespace com::sun::star; @@ -26,19 +27,22 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { - TextHierarchyLinePrimitive2D::TextHierarchyLinePrimitive2D(const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + TextHierarchyLinePrimitive2D::TextHierarchyLinePrimitive2D(Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyLinePrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D) + sal_uInt32 TextHierarchyLinePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D; + } TextHierarchyParagraphPrimitive2D::TextHierarchyParagraphPrimitive2D( - const Primitive2DContainer& rChildren, + Primitive2DContainer&& aChildren, sal_Int16 nOutlineLevel) - : GroupPrimitive2D(rChildren), + : GroupPrimitive2D(std::move(aChildren)), mnOutlineLevel(nOutlineLevel) { } @@ -56,35 +60,43 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyParagraphPrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D) + sal_uInt32 TextHierarchyParagraphPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D; + } - TextHierarchyBulletPrimitive2D::TextHierarchyBulletPrimitive2D(const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + TextHierarchyBulletPrimitive2D::TextHierarchyBulletPrimitive2D(Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyBulletPrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D) + sal_uInt32 TextHierarchyBulletPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D; + } - TextHierarchyBlockPrimitive2D::TextHierarchyBlockPrimitive2D(const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + TextHierarchyBlockPrimitive2D::TextHierarchyBlockPrimitive2D(Primitive2DContainer&& aChildren) + : GroupPrimitive2D(std::move(aChildren)) { } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyBlockPrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D) + sal_uInt32 TextHierarchyBlockPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D; + } TextHierarchyFieldPrimitive2D::TextHierarchyFieldPrimitive2D( - const Primitive2DContainer& rChildren, + Primitive2DContainer&& aChildren, const FieldType& rFieldType, const std::vector< std::pair< OUString, OUString>>* pNameValue) - : GroupPrimitive2D(rChildren), - meType(rFieldType), - meNameValue() + : GroupPrimitive2D(std::move(aChildren)), + meType(rFieldType) { if (nullptr != pNameValue) { @@ -119,14 +131,44 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyFieldPrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D) - TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren) + sal_uInt32 TextHierarchyFieldPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D; + } + + + TextHierarchyEditPrimitive2D::TextHierarchyEditPrimitive2D(Primitive2DContainer&& aContent) + : GroupPrimitive2D(std::move(aContent)) + { + } + + void TextHierarchyEditPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const + { + // check if TextEdit is active. If not, process. If yes, suppress the content + // lok case: always decompose it when we're rendering a slide show + if (!rViewInformation.getTextEditActive() || comphelper::LibreOfficeKit::isSlideshowRendering()) + GroupPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + // provide unique ID + sal_uInt32 TextHierarchyEditPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D; + } + + + TextHierarchyEmphasisMarkPrimitive2D::TextHierarchyEmphasisMarkPrimitive2D(Primitive2DContainer&& aContent) + : GroupPrimitive2D(std::move(aContent)) { } // provide unique ID - ImplPrimitive2DIDBlock(TextHierarchyEditPrimitive2D, PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D) + sal_uInt32 TextHierarchyEmphasisMarkPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index e3e57b2b5997..e92732fd1751 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -22,6 +22,8 @@ #include <algorithm> #include <com/sun/star/uno/XComponentContext.hpp> + +#include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/attribute/fontattribute.hxx> #include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <comphelper/processfactory.hxx> @@ -29,455 +31,496 @@ #include <osl/diagnose.h> #include <tools/gen.hxx> #include <vcl/canvastools.hxx> +#include <vcl/kernarray.hxx> #include <vcl/timer.hxx> #include <vcl/virdev.hxx> #include <vcl/font.hxx> #include <vcl/metric.hxx> #include <i18nlangtag/languagetag.hxx> #include <vcl/svapp.hxx> +#include <vcl/vcllayout.hxx> +#include <vcl/glyphitemcache.hxx> +namespace drawinglayer::primitive2d +{ +namespace +{ +class ImpTimedRefDev; // VDev RevDevice provider -namespace +//the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor +//or disposing of the default XComponentContext which causes the underlying +//OutputDevice to get released + +//The ImpTimerRefDev itself, if the timeout ever gets hit, will call +//reset on the scoped_timed_RefDev to release the ImpTimerRefDev early +//if it's unused for a few minutes +class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev> { - class ImpTimedRefDev; +public: + scoped_timed_RefDev() + : comphelper::unique_disposing_ptr<ImpTimedRefDev>( + (css::uno::Reference<css::lang::XComponent>( + ::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW))) + { + } +}; - //the scoped_timed_RefDev owns an ImpTimeRefDev and releases it on dtor - //or disposing of the default XComponentContext which causes the underlying - //OutputDevice to get released +class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev> +{ +}; - //The ImpTimerRefDev itself, if the timeout ever gets hit, will call - //reset on the scoped_timed_RefDev to release the ImpTimerRefDev early - //if it's unused for a few minutes - class scoped_timed_RefDev : public comphelper::unique_disposing_ptr<ImpTimedRefDev> - { - public: - scoped_timed_RefDev() : comphelper::unique_disposing_ptr<ImpTimedRefDev>((css::uno::Reference<css::lang::XComponent>(::comphelper::getProcessComponentContext(), css::uno::UNO_QUERY_THROW))) - { - } - }; +class ImpTimedRefDev : public Timer +{ + scoped_timed_RefDev& mrOwnerOfMe; + VclPtr<VirtualDevice> mpVirDev; + sal_uInt32 mnUseCount; + +public: + explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe); + virtual ~ImpTimedRefDev() override; + virtual void Invoke() override; + + VirtualDevice& acquireVirtualDevice(); + void releaseVirtualDevice(); +}; + +ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe) + : Timer("drawinglayer ImpTimedRefDev destroy mpVirDev") + , mrOwnerOfMe(rOwnerOfMe) + , mpVirDev(nullptr) + , mnUseCount(0) +{ + SetTimeout(3L * 60L * 1000L); // three minutes + Start(); +} - class the_scoped_timed_RefDev : public rtl::Static<scoped_timed_RefDev, the_scoped_timed_RefDev> {}; +ImpTimedRefDev::~ImpTimedRefDev() +{ + OSL_ENSURE(0 == mnUseCount, "destruction of a still used ImpTimedRefDev (!)"); + const SolarMutexGuard aSolarGuard; + mpVirDev.disposeAndClear(); +} - class ImpTimedRefDev : public Timer - { - scoped_timed_RefDev& mrOwnerOfMe; - VclPtr<VirtualDevice> mpVirDev; - sal_uInt32 mnUseCount; - - public: - explicit ImpTimedRefDev(scoped_timed_RefDev& rOwnerofMe); - virtual ~ImpTimedRefDev() override; - virtual void Invoke() override; - - VirtualDevice& acquireVirtualDevice(); - void releaseVirtualDevice(); - }; - - ImpTimedRefDev::ImpTimedRefDev(scoped_timed_RefDev& rOwnerOfMe) - : Timer( "drawinglayer ImpTimedRefDev destroy mpVirDev" ), - mrOwnerOfMe(rOwnerOfMe), - mpVirDev(nullptr), - mnUseCount(0) +void ImpTimedRefDev::Invoke() +{ + // for obvious reasons, do not call anything after this + mrOwnerOfMe.reset(); +} + +VirtualDevice& ImpTimedRefDev::acquireVirtualDevice() +{ + if (!mpVirDev) { - SetTimeout(3L * 60L * 1000L); // three minutes - Start(); + mpVirDev = VclPtr<VirtualDevice>::Create(); + mpVirDev->SetReferenceDevice(VirtualDevice::RefDevMode::MSO1); } - ImpTimedRefDev::~ImpTimedRefDev() + if (!mnUseCount) { - OSL_ENSURE(0 == mnUseCount, "destruction of a still used ImpTimedRefDev (!)"); - const SolarMutexGuard aSolarGuard; - mpVirDev.disposeAndClear(); + Stop(); } - void ImpTimedRefDev::Invoke() + mnUseCount++; + + return *mpVirDev; +} + +void ImpTimedRefDev::releaseVirtualDevice() +{ + OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)"); + mnUseCount--; + + if (!mnUseCount) { - // for obvious reasons, do not call anything after this - mrOwnerOfMe.reset(); + Start(); } +} - VirtualDevice& ImpTimedRefDev::acquireVirtualDevice() - { - if(!mpVirDev) - { - mpVirDev = VclPtr<VirtualDevice>::Create(); - mpVirDev->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 ); - } +VirtualDevice& acquireGlobalVirtualDevice() +{ + scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); - if(!mnUseCount) - { - Stop(); - } + if (!rStdRefDevice) + rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice)); - mnUseCount++; + return rStdRefDevice->acquireVirtualDevice(); +} - return *mpVirDev; - } +void releaseGlobalVirtualDevice() +{ + scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); - void ImpTimedRefDev::releaseVirtualDevice() - { - OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)"); - mnUseCount--; + OSL_ENSURE(rStdRefDevice, + "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)"); + rStdRefDevice->releaseVirtualDevice(); +} - if(!mnUseCount) - { - Start(); - } - } } // end of anonymous namespace +TextLayouterDevice::TextLayouterDevice() + : mrDevice(acquireGlobalVirtualDevice()) +{ + // tdf#168002 activate SubpixelPositioning for al TextLayouterDevice-calls + mrDevice.setSubpixelPositioning(true); +} -// access to one global ImpTimedRefDev incarnation in namespace drawinglayer::primitive +TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE { releaseGlobalVirtualDevice(); } -namespace drawinglayer::primitive2d +void TextLayouterDevice::setFont(const vcl::Font& rFont) { - // static methods here - static VirtualDevice& acquireGlobalVirtualDevice() - { - scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); + mrDevice.SetFont(rFont); + mnFontScalingFixX = 1.0; + mnFontScalingFixY = 1.0; +} + +void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontAttribute, + double fFontScaleX, double fFontScaleY, + const css::lang::Locale& rLocale) +{ + vcl::Font aFont + = getVclFontFromFontAttribute(rFontAttribute, fFontScaleX, fFontScaleY, 0.0, rLocale); + setFont(aFont); + Size aFontSize = aFont.GetFontSize(); + if (aFontSize.Height()) + { + mnFontScalingFixY = fFontScaleY / aFontSize.Height(); + // aFontSize.Width() is 0 for uninformly scaled fonts: see getVclFontFromFontAttribute + mnFontScalingFixX + = fFontScaleX / (aFontSize.Width() ? aFontSize.Width() : aFontSize.Height()); + } + else + { + mnFontScalingFixX = mnFontScalingFixY = 1.0; + } +} - if(!rStdRefDevice) - rStdRefDevice.reset(new ImpTimedRefDev(rStdRefDevice)); +void TextLayouterDevice::setLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode) +{ + mrDevice.SetLayoutMode(nTextLayoutMode); +} - return rStdRefDevice->acquireVirtualDevice(); - } +vcl::text::ComplexTextLayoutFlags TextLayouterDevice::getLayoutMode() const +{ + return mrDevice.GetLayoutMode(); +} - static void releaseGlobalVirtualDevice() - { - scoped_timed_RefDev& rStdRefDevice = the_scoped_timed_RefDev::get(); +void TextLayouterDevice::setTextColor(const basegfx::BColor& rColor) +{ + mrDevice.SetTextColor(Color(rColor)); +} - OSL_ENSURE(rStdRefDevice, "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)"); - rStdRefDevice->releaseVirtualDevice(); - } +double TextLayouterDevice::getOverlineOffset() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + double fRet = (aMetric.GetInternalLeading() / 2.0) - aMetric.GetAscent(); + return fRet * mnFontScalingFixY; +} - TextLayouterDevice::TextLayouterDevice() - : maSolarGuard(), - mrDevice(acquireGlobalVirtualDevice()) - { - } +double TextLayouterDevice::getUnderlineOffset() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + double fRet = aMetric.GetDescent() / 2.0; + return fRet * mnFontScalingFixY; +} - TextLayouterDevice::~TextLayouterDevice() COVERITY_NOEXCEPT_FALSE - { - releaseGlobalVirtualDevice(); - } +double TextLayouterDevice::getStrikeoutOffset() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + double fRet = (aMetric.GetAscent() - aMetric.GetInternalLeading()) / 3.0; + return fRet * mnFontScalingFixY; +} - void TextLayouterDevice::setFont(const vcl::Font& rFont) - { - mrDevice.SetFont( rFont ); - } +double TextLayouterDevice::getOverlineHeight() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + double fRet = aMetric.GetInternalLeading() / 2.5; + return fRet * mnFontScalingFixY; +} - void TextLayouterDevice::setFontAttribute( - const attribute::FontAttribute& rFontAttribute, - double fFontScaleX, - double fFontScaleY, - const css::lang::Locale& rLocale) - { - setFont(getVclFontFromFontAttribute( - rFontAttribute, - fFontScaleX, - fFontScaleY, - 0.0, - rLocale)); - } +double TextLayouterDevice::getUnderlineHeight() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + double fRet = aMetric.GetDescent() / 4.0; + return fRet * mnFontScalingFixY; +} - double TextLayouterDevice::getOverlineOffset() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - double fRet = (rMetric.GetInternalLeading() / 2.0) - rMetric.GetAscent(); - return fRet; - } +double TextLayouterDevice::getTextHeight() const +{ + return mrDevice.GetTextHeightDouble() * mnFontScalingFixY; +} - double TextLayouterDevice::getUnderlineOffset() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - double fRet = rMetric.GetDescent() / 2.0; - return fRet; - } +double TextLayouterDevice::getTextWidth(const OUString& rText, sal_uInt32 nIndex, + sal_uInt32 nLength) const +{ + return mrDevice.GetTextWidthDouble(rText, nIndex, nLength) * mnFontScalingFixX; +} - double TextLayouterDevice::getStrikeoutOffset() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - double fRet = (rMetric.GetAscent() - rMetric.GetInternalLeading()) / 3.0; - return fRet; - } +void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, + const OUString& rText, sal_uInt32 nIndex, + sal_uInt32 nLength, const std::vector<double>& rDXArray, + const std::vector<sal_Bool>& rKashidaArray) const +{ + const sal_uInt32 nDXArrayCount(rDXArray.size()); + sal_uInt32 nTextLength(nLength); + const sal_uInt32 nStringLength(rText.getLength()); - double TextLayouterDevice::getOverlineHeight() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - double fRet = rMetric.GetInternalLeading() / 2.5; - return fRet; - } + if (nTextLength + nIndex > nStringLength) + { + nTextLength = nStringLength - nIndex; + } - double TextLayouterDevice::getUnderlineHeight() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - double fRet = rMetric.GetDescent() / 4.0; - return fRet; - } + if (nDXArrayCount) + { + OSL_ENSURE(nDXArrayCount == nTextLength, + "DXArray size does not correspond to text portion size (!)"); - double TextLayouterDevice::getTextHeight() const - { - return mrDevice.GetTextHeight(); - } + mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength, 0, rDXArray, + rKashidaArray); + } + else + { + mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, nLength); + } + if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) + || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) + { + auto scale = basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY); + for (auto& poly : rB2DPolyPolyVector) + poly.transform(scale); + } +} - double TextLayouterDevice::getTextWidth( - const OUString& rText, - sal_uInt32 nIndex, - sal_uInt32 nLength) const - { - return mrDevice.GetTextWidth(rText, nIndex, nLength); - } +basegfx::B2DRange TextLayouterDevice::getTextBoundRect(const OUString& rText, sal_uInt32 nIndex, + sal_uInt32 nLength) const +{ + sal_uInt32 nTextLength(nLength); + const sal_uInt32 nStringLength(rText.getLength()); - void TextLayouterDevice::getTextOutlines( - basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, - const OUString& rText, - sal_uInt32 nIndex, - sal_uInt32 nLength, - const std::vector< double >& rDXArray) const - { - const sal_uInt32 nDXArrayCount(rDXArray.size()); - sal_uInt32 nTextLength(nLength); - const sal_uInt32 nStringLength(rText.getLength()); - - if(nTextLength + nIndex > nStringLength) - { - nTextLength = nStringLength - nIndex; - } - - if(nDXArrayCount) - { - OSL_ENSURE(nDXArrayCount == nTextLength, "DXArray size does not correspond to text portion size (!)"); - std::vector< long > aIntegerDXArray(nDXArrayCount); - - for(sal_uInt32 a(0); a < nDXArrayCount; a++) - { - aIntegerDXArray[a] = basegfx::fround(rDXArray[a]); - } - - mrDevice.GetTextOutlines( - rB2DPolyPolyVector, - rText, - nIndex, - nIndex, - nLength, - 0, - aIntegerDXArray.data()); - } - else - { - mrDevice.GetTextOutlines( - rB2DPolyPolyVector, - rText, - nIndex, - nIndex, - nLength); - } - } + if (nTextLength + nIndex > nStringLength) + { + nTextLength = nStringLength - nIndex; + } - basegfx::B2DRange TextLayouterDevice::getTextBoundRect( - const OUString& rText, - sal_uInt32 nIndex, - sal_uInt32 nLength) const + if (nTextLength) + { + basegfx::B2DRange aRect; + mrDevice.GetTextBoundRect(aRect, rText, nIndex, nIndex, nLength); + if (!rtl_math_approxEqual(mnFontScalingFixY, 1.0) + || !rtl_math_approxEqual(mnFontScalingFixX, 1.0)) { - sal_uInt32 nTextLength(nLength); - const sal_uInt32 nStringLength(rText.getLength()); - - if(nTextLength + nIndex > nStringLength) - { - nTextLength = nStringLength - nIndex; - } - - if(nTextLength) - { - ::tools::Rectangle aRect; - - mrDevice.GetTextBoundRect( - aRect, - rText, - nIndex, - nIndex, - nLength); - - // #i104432#, #i102556# take empty results into account - if(!aRect.IsEmpty()) - { - return vcl::unotools::b2DRectangleFromRectangle(aRect); - } - } - - return basegfx::B2DRange(); + aRect.transform( + basegfx::utils::createScaleB2DHomMatrix(mnFontScalingFixX, mnFontScalingFixY)); } + return aRect; + } - double TextLayouterDevice::getFontAscent() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - return rMetric.GetAscent(); - } + return basegfx::B2DRange(); +} - double TextLayouterDevice::getFontDescent() const - { - const ::FontMetric& rMetric = mrDevice.GetFontMetric(); - return rMetric.GetDescent(); - } +double TextLayouterDevice::getFontAscent() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + return aMetric.GetAscent() * mnFontScalingFixY; +} - void TextLayouterDevice::addTextRectActions( - const ::tools::Rectangle& rRectangle, - const OUString& rText, - DrawTextFlags nStyle, - GDIMetaFile& rGDIMetaFile) const - { - mrDevice.AddTextRectActions( - rRectangle, rText, nStyle, rGDIMetaFile); - } +double TextLayouterDevice::getFontDescent() const +{ + const ::FontMetric aMetric = mrDevice.GetFontMetric(); + return aMetric.GetDescent() * mnFontScalingFixY; +} - std::vector< double > TextLayouterDevice::getTextArray( - const OUString& rText, - sal_uInt32 nIndex, - sal_uInt32 nLength) const - { - std::vector< double > aRetval; - sal_uInt32 nTextLength(nLength); - const sal_uInt32 nStringLength(rText.getLength()); - - if(nTextLength + nIndex > nStringLength) - { - nTextLength = nStringLength - nIndex; - } - - if(nTextLength) - { - aRetval.reserve(nTextLength); - std::vector<long> aArray(nTextLength); - mrDevice.GetTextArray(rText, aArray.data(), nIndex, nLength); - aRetval.assign(aArray.begin(), aArray.end()); - } - - return aRetval; - } +void TextLayouterDevice::addTextRectActions(const ::tools::Rectangle& rRectangle, + const OUString& rText, DrawTextFlags nStyle, + GDIMetaFile& rGDIMetaFile) const +{ + mrDevice.AddTextRectActions(rRectangle, rText, nStyle, rGDIMetaFile); +} +std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_uInt32 nIndex, + sal_uInt32 nLength, bool bCaret) const +{ + std::vector<double> aRetval; + sal_uInt32 nTextLength(nLength); + const sal_uInt32 nStringLength(rText.getLength()); + + if (nTextLength + nIndex > nStringLength) + { + nTextLength = nStringLength - nIndex; + } + + if (nTextLength) + { + KernArray aArray; + mrDevice.GetTextArray(rText, &aArray, nIndex, nTextLength, bCaret); + aRetval.reserve(aArray.size()); + for (size_t i = 0, nEnd = aArray.size(); i < nEnd; ++i) + aRetval.push_back(aArray[i] * mnFontScalingFixX); + } + + return aRetval; +} + +std::unique_ptr<SalLayout> +TextLayouterDevice::getSalLayout(const OUString& rText, sal_uInt32 nIndex, sal_uInt32 nLength, + const basegfx::B2DPoint& rStartPoint, const KernArray& rDXArray, + std::span<const sal_Bool> pKashidaAry) const +{ + const SalLayoutGlyphs* pGlyphs( + SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&mrDevice, rText, nIndex, nLength)); + const Point aStartPoint(basegfx::fround<tools::Long>(rStartPoint.getX()), + basegfx::fround<tools::Long>(rStartPoint.getY())); + return mrDevice.ImplLayout(rText, nIndex, nLength, aStartPoint, 0, rDXArray, pKashidaAry, + SalLayoutFlags::NONE, nullptr, pGlyphs); +} + +void TextLayouterDevice::createEmphasisMarks( + const SalLayout& rSalLayout, TextEmphasisMark aTextEmphasisMark, bool bAbove, + const std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&, bool, + const tools::Rectangle&, const tools::Rectangle&)>& rCallback) const +{ + FontEmphasisMark nEmphasisMark(FontEmphasisMark::NONE); + double fEmphasisHeight(getTextHeight() * (250.0 / 1000.0)); + + switch (aTextEmphasisMark) + { + case TEXT_FONT_EMPHASIS_MARK_DOT: + nEmphasisMark = FontEmphasisMark::Dot; + break; + case TEXT_FONT_EMPHASIS_MARK_CIRCLE: + nEmphasisMark = FontEmphasisMark::Circle; + break; + case TEXT_FONT_EMPHASIS_MARK_DISC: + nEmphasisMark = FontEmphasisMark::Disc; + break; + case TEXT_FONT_EMPHASIS_MARK_ACCENT: + nEmphasisMark = FontEmphasisMark::Accent; + break; + default: + break; + } + + if (bAbove) + nEmphasisMark |= FontEmphasisMark::PosAbove; + else + nEmphasisMark |= FontEmphasisMark::PosBelow; + + mrDevice.createEmphasisMarks(nEmphasisMark, static_cast<tools::Long>(fEmphasisHeight), + rSalLayout, rCallback); +} // helper methods for vcl font handling - vcl::Font getVclFontFromFontAttribute( - const attribute::FontAttribute& rFontAttribute, - double fFontScaleX, - double fFontScaleY, - double fFontRotation, - const css::lang::Locale& rLocale) - { - // detect FontScaling - const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY))); - const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX))); - const bool bFontIsScaled(nHeight != nWidth); +vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute, + double fFontScaleX, double fFontScaleY, double fFontRotation, + const css::lang::Locale& rLocale) +{ + // detect FontScaling + const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY))); + const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX))); + const bool bFontIsScaled(nHeight != nWidth); #ifdef _WIN32 - // for WIN32 systems, start with creating an unscaled font. If FontScaling - // is wanted, that width needs to be adapted using FontMetric again to get a - // width of the unscaled font - vcl::Font aRetval( - rFontAttribute.getFamilyName(), - rFontAttribute.getStyleName(), - Size(0, nHeight)); + // for WIN32 systems, start with creating an unscaled font. If FontScaling + // is wanted, that width needs to be adapted using FontMetric again to get a + // width of the unscaled font + vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(), + Size(0, nHeight)); #else - // for non-WIN32 systems things are easier since these accept a Font creation - // with initially nWidth != nHeight for FontScaling. Despite that, use zero for - // FontWidth when no scaling is used to explicitly have that zero when e.g. the - // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a - // set FontWidth; import that in a WIN32 system, and trouble is there) - vcl::Font aRetval( - rFontAttribute.getFamilyName(), - rFontAttribute.getStyleName(), - Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight)); + // for non-WIN32 systems things are easier since these accept a Font creation + // with initially nWidth != nHeight for FontScaling. Despite that, use zero for + // FontWidth when no scaling is used to explicitly have that zero when e.g. the + // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a + // set FontWidth; import that in a WIN32 system, and trouble is there) + vcl::Font aRetval(rFontAttribute.getFamilyName(), rFontAttribute.getStyleName(), + Size(bFontIsScaled ? std::max<sal_uInt32>(nWidth, 1) : 0, nHeight)); #endif - // define various other FontAttribute - aRetval.SetAlignment(ALIGN_BASELINE); - aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE); - aRetval.SetVertical(rFontAttribute.getVertical()); - aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight())); - aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE); - aRetval.SetOutline(rFontAttribute.getOutline()); - aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE); - aRetval.SetLanguage(LanguageTag::convertToLanguageType( rLocale, false)); + // define various other FontAttribute + aRetval.SetAlignment(ALIGN_BASELINE); + aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL + : RTL_TEXTENCODING_UNICODE); + aRetval.SetVertical(rFontAttribute.getVertical()); + aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight())); + aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE); + aRetval.SetOutline(rFontAttribute.getOutline()); + aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE); + aRetval.SetLanguage(LanguageTag::convertToLanguageType(rLocale, false)); #ifdef _WIN32 - // for WIN32 systems, correct the FontWidth if FontScaling is used - if(bFontIsScaled && nHeight > 0) - { - const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aRetval)); - - if(aUnscaledFontMetric.GetAverageFontWidth() > 0) - { - const double fScaleFactor(static_cast<double>(nWidth) / static_cast<double>(nHeight)); - const sal_uInt32 nScaledWidth(basegfx::fround(static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor)); - aRetval.SetAverageFontWidth(nScaledWidth); - } - } -#endif - // handle FontRotation (if defined) - if(!basegfx::fTools::equalZero(fFontRotation)) - { - sal_Int16 aRotate10th(static_cast<sal_Int16>(fFontRotation * (-1800.0/F_PI))); - aRetval.SetOrientation(aRotate10th % 3600); - } - - return aRetval; - } + // for WIN32 systems, correct the FontWidth if FontScaling is used + if (bFontIsScaled && nHeight > 0) + { + const FontMetric aUnscaledFontMetric( + Application::GetDefaultDevice()->GetFontMetric(aRetval)); - attribute::FontAttribute getFontAttributeFromVclFont( - basegfx::B2DVector& o_rSize, - const vcl::Font& rFont, - bool bRTL, - bool bBiDiStrong) + if (aUnscaledFontMetric.GetAverageFontWidth() > 0) { - const attribute::FontAttribute aRetval( - rFont.GetFamilyName(), - rFont.GetStyleName(), - static_cast<sal_uInt16>(rFont.GetWeight()), - RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), - rFont.IsVertical(), - ITALIC_NONE != rFont.GetItalic(), - PITCH_FIXED == rFont.GetPitch(), - rFont.IsOutline(), - bRTL, - bBiDiStrong); - // TODO: eKerning - - // set FontHeight and init to no FontScaling - o_rSize.setY(std::max<long>(rFont.GetFontSize().getHeight(), 0)); - o_rSize.setX(o_rSize.getY()); + const double fScaleFactor(static_cast<double>(nWidth) / static_cast<double>(nHeight)); + const sal_uInt32 nScaledWidth(basegfx::fround( + static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth()) * fScaleFactor)); + aRetval.SetAverageFontWidth(nScaledWidth); + } + } +#endif + // handle FontRotation (if defined) + if (!basegfx::fTools::equalZero(fFontRotation)) + { + int aRotate10th(-basegfx::rad2deg<10>(fFontRotation)); + aRetval.SetOrientation(Degree10(aRotate10th % 3600)); + } + + return aRetval; +} + +attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector& o_rSize, + const vcl::Font& rFont, bool bRTL, + bool bBiDiStrong) +{ + const attribute::FontAttribute aRetval( + rFont.GetFamilyName(), rFont.GetStyleName(), static_cast<sal_uInt16>(rFont.GetWeight()), + RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), rFont.IsVertical(), + ITALIC_NONE != rFont.GetItalic(), PITCH_FIXED == rFont.GetPitch(), rFont.IsOutline(), bRTL, + bBiDiStrong); + // TODO: eKerning + + // set FontHeight and init to no FontScaling + o_rSize.setY(std::max<tools::Long>(rFont.GetFontSize().getHeight(), 0)); + o_rSize.setX(o_rSize.getY()); #ifdef _WIN32 - // for WIN32 systems, the FontScaling at the Font is detected by - // checking that FontWidth != 0. When FontScaling is used, WIN32 - // needs to do extra stuff to detect the correct width (since it's - // zero and not equal the font height) and its relationship to - // the height - if(rFont.GetFontSize().getWidth() > 0) - { - vcl::Font aUnscaledFont(rFont); - aUnscaledFont.SetAverageFontWidth(0); - const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont)); - - if(aUnscaledFontMetric.GetAverageFontWidth() > 0) - { - const double fScaleFactor(static_cast<double>(rFont.GetFontSize().getWidth()) / static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth())); - o_rSize.setX(fScaleFactor * o_rSize.getY()); - } - } + // for WIN32 systems, the FontScaling at the Font is detected by + // checking that FontWidth != 0. When FontScaling is used, WIN32 + // needs to do extra stuff to detect the correct width (since it's + // zero and not equal the font height) and its relationship to + // the height + if (rFont.GetFontSize().getWidth() > 0) + { + vcl::Font aUnscaledFont(rFont); + aUnscaledFont.SetAverageFontWidth(0); + const FontMetric aUnscaledFontMetric( + Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont)); + + if (aUnscaledFontMetric.GetAverageFontWidth() > 0) + { + const double fScaleFactor( + static_cast<double>(rFont.GetFontSize().getWidth()) + / static_cast<double>(aUnscaledFontMetric.GetAverageFontWidth())); + o_rSize.setX(fScaleFactor * o_rSize.getY()); + } + } #else - // For non-WIN32 systems the detection is the same, but the value - // is easier achieved since width == height is interpreted as no - // scaling. Ergo, Width == 0 means width == height, and width != 0 - // means the scaling is in the direct relation of width to height - if(rFont.GetFontSize().getWidth() > 0) - { - o_rSize.setX(static_cast<double>(rFont.GetFontSize().getWidth())); - } + // For non-WIN32 systems the detection is the same, but the value + // is easier achieved since width == height is interpreted as no + // scaling. Ergo, Width == 0 means width == height, and width != 0 + // means the scaling is in the direct relation of width to height + if (rFont.GetFontSize().getWidth() > 0) + { + o_rSize.setX(static_cast<double>(rFont.GetFontSize().getWidth())); + } #endif - return aRetval; - } + return aRetval; +} } // end of namespace diff --git a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx index 2238fc383d5c..362c2e9778fb 100644 --- a/drawinglayer/source/primitive2d/textlineprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/textlineprimitive2d.cxx @@ -22,16 +22,17 @@ #include <drawinglayer/attribute/strokeattribute.hxx> #include <drawinglayer/attribute/lineattribute.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx> +#include <utility> namespace drawinglayer::primitive2d { - void TextLinePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference TextLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { if(TEXT_LINE_NONE == getTextLine()) - return; + return nullptr; bool bDoubleLine(false); bool bWaveLine(false); @@ -89,10 +90,6 @@ namespace drawinglayer::primitive2d break; } case TEXT_LINE_SMALLWAVE: - { - bWaveLine = true; - break; - } case TEXT_LINE_WAVE: { bWaveLine = true; @@ -177,7 +174,7 @@ namespace drawinglayer::primitive2d aDoubleArray.push_back(static_cast<double>(*p) * fHeight); } - aStrokeAttribute = attribute::StrokeAttribute(aDoubleArray); + aStrokeAttribute = attribute::StrokeAttribute(std::move(aDoubleArray)); } // create base polygon and new primitive @@ -207,18 +204,19 @@ namespace drawinglayer::primitive2d fWaveWidth *= 2.0; } - aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5)); + aNewPrimitive = new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5); } else { - aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aLine, aLineAttribute, aStrokeAttribute)); + aNewPrimitive = new PolygonStrokePrimitive2D(std::move(aLine), aLineAttribute, std::move(aStrokeAttribute)); } - // add primitive - rContainer.push_back(aNewPrimitive); - if(!bDoubleLine) - return; + return aNewPrimitive; + + // add primitive + Primitive2DContainer aContainer; + aContainer.push_back(aNewPrimitive); // double line, create 2nd primitive with offset using TransformPrimitive based on // already created NewPrimitive @@ -242,19 +240,19 @@ namespace drawinglayer::primitive2d aTransform.translate(aTranslate.getX(), aTranslate.getY()); // add transform primitive - const Primitive2DContainer aContent { aNewPrimitive }; - rContainer.push_back( new TransformPrimitive2D(aTransform, aContent) ); + Primitive2DContainer aContent { aNewPrimitive }; + aContainer.push_back(new TransformPrimitive2D(aTransform, std::move(aContent))); + return new GroupPrimitive2D(std::move(aContainer)); } TextLinePrimitive2D::TextLinePrimitive2D( - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, double fWidth, double fOffset, double fHeight, TextLine eTextLine, const basegfx::BColor& rLineColor) - : BufferedDecompositionPrimitive2D(), - maObjectTransformation(rObjectTransformation), + : maObjectTransformation(std::move(aObjectTransformation)), mfWidth(fWidth), mfOffset(fOffset), mfHeight(fHeight), @@ -281,7 +279,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextLinePrimitive2D, PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D) + sal_uInt32 TextLinePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx index e85d61009632..fae5866c6517 100644 --- a/drawinglayer/source/primitive2d/textprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx @@ -22,307 +22,352 @@ #include <basegfx/polygon/b2dpolypolygon.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/groupprimitive2d.hxx> #include <primitive2d/texteffectprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> - +#include <vcl/vcllayout.hxx> +#include <vcl/rendercontext/State.hxx> +#include <vcl/kernarray.hxx> +#include <utility> +#include <osl/diagnose.h> using namespace com::sun::star; - +namespace drawinglayer::primitive2d +{ namespace { - // adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted - // scale from a text transformation. A copy is modified so that it contains only positive - // scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter. - // rScale is adapted accordingly to contain the corrected scale which would need to be - // applied to e.g. outlines received from TextLayouter under usage of fontScale. This - // includes Y-Scale, X-Scale-correction and mirrorings. - basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale) +// adapts fontScale for usage with TextLayouter. Input is rScale which is the extracted +// scale from a text transformation. A copy is modified so that it contains only positive +// scalings and XY-equal scalings to allow to get a non-X-scaled Vcl-Font for TextLayouter. +// rScale is adapted accordingly to contain the corrected scale which would need to be +// applied to e.g. outlines received from TextLayouter under usage of fontScale. This +// includes Y-Scale, X-Scale-correction and mirrorings. +basegfx::B2DVector getCorrectedScaleAndFontScale(basegfx::B2DVector& rScale) +{ + // copy input value + basegfx::B2DVector aFontScale(rScale); + + // correct FontHeight settings + if (basegfx::fTools::equalZero(aFontScale.getY())) + { + // no font height; choose one and adapt scale to get back to original scaling + static const double fDefaultFontScale(100.0); + rScale.setY(1.0 / fDefaultFontScale); + aFontScale.setY(fDefaultFontScale); + } + else if (aFontScale.getY() < 0.0) + { + // negative font height; invert and adapt scale to get back to original scaling + aFontScale.setY(-aFontScale.getY()); + rScale.setY(-1.0); + } + else { - // copy input value - basegfx::B2DVector aFontScale(rScale); + // positive font height; adapt scale; scaling will be part of the polygons + rScale.setY(1.0); + } - // correct FontHeight settings - if(basegfx::fTools::equalZero(aFontScale.getY())) - { - // no font height; choose one and adapt scale to get back to original scaling - static const double fDefaultFontScale(100.0); - rScale.setY(1.0 / fDefaultFontScale); - aFontScale.setY(fDefaultFontScale); - } - else if(basegfx::fTools::less(aFontScale.getY(), 0.0)) - { - // negative font height; invert and adapt scale to get back to original scaling - aFontScale.setY(-aFontScale.getY()); - rScale.setY(-1.0); - } - else - { - // positive font height; adapt scale; scaling will be part of the polygons - rScale.setY(1.0); - } + // correct FontWidth settings + if (basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY())) + { + // no FontScale, adapt scale + rScale.setX(1.0); + } + else + { + // If FontScale is used, force to no FontScale to get a non-scaled VCL font. + // Adapt scaling in X accordingly. + rScale.setX(aFontScale.getX() / aFontScale.getY()); + aFontScale.setX(aFontScale.getY()); + } - // correct FontWidth settings - if(basegfx::fTools::equal(aFontScale.getX(), aFontScale.getY())) - { - // no FontScale, adapt scale - rScale.setX(1.0); - } - else + return aFontScale; +} +} // end of anonymous namespace + +void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation( + basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const +{ + if (!getTextLength()) + return; + + // decompose object transformation to single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + + // if decomposition returns false, create no geometry since e.g. scaling may + // be zero + if (!(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX) + && aScale.getX() != 0.0)) + return; + + // handle special case: If scale is negative in (x,y) (3rd quadrant), it can + // be expressed as rotation by PI + if (aScale.getX() < 0.0 && aScale.getY() < 0.0) + { + aScale = basegfx::absolute(aScale); + fRotate += M_PI; + } + + // for the TextLayouterDevice, it is necessary to have a scaling representing + // the font size. Since we want to extract polygons here, it is okay to + // work just with scaling and to ignore shear, rotation and translation, + // all that can be applied to the polygons later + const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale)); + + // prepare textlayoutdevice + TextLayouterDevice aTextLayouter; + aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(), + getLocale()); + + // When getting outlines from stretched text (aScale.getX() != 1.0) it + // is necessary to inverse-scale the DXArray (if used) to not get the + // outlines already aligned to given, but wrong DXArray + if (!getDXArray().empty() && !basegfx::fTools::equal(aScale.getX(), 1.0)) + { + std::vector<double> aScaledDXArray = getDXArray(); + const double fDXArrayScale(1.0 / aScale.getX()); + + for (double& a : aScaledDXArray) { - // If FontScale is used, force to no FontScale to get a non-scaled VCL font. - // Adapt scaling in X accordingly. - rScale.setX(aFontScale.getX() / aFontScale.getY()); - aFontScale.setX(aFontScale.getY()); + a *= fDXArrayScale; } - return aFontScale; + // get the text outlines + aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(), + aScaledDXArray, getKashidaArray()); + } + else + { + // get the text outlines + aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), getTextLength(), + getDXArray(), getKashidaArray()); } -} // end of anonymous namespace + // create primitives for the outlines + const sal_uInt32 nCount(rTarget.size()); -namespace drawinglayer::primitive2d + if (nCount) + { + // prepare object transformation for polygons + rTransformation = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, fShearX, fRotate, aTranslate); + } +} + +Primitive2DReference TextSimplePortionPrimitive2D::create2DDecomposition( + const geometry::ViewInformation2D& /*rViewInformation*/) const { - void TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(basegfx::B2DPolyPolygonVector& rTarget, basegfx::B2DHomMatrix& rTransformation) const - { - if(!getTextLength()) - return; + if (!getTextLength()) + return nullptr; - // decompose object transformation to single values - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; + basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; + basegfx::B2DHomMatrix aPolygonTransform; - // if decomposition returns false, create no geometry since e.g. scaling may - // be zero - if (!(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX) && aScale.getX() != 0.0)) - return; + // get text outlines and their object transformation + getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform); - // handle special case: If scale is negative in (x,y) (3rd quadrant), it can - // be expressed as rotation by PI - if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0)) - { - aScale = basegfx::absolute(aScale); - fRotate += F_PI; - } + // create primitives for the outlines + const sal_uInt32 nCount(aB2DPolyPolyVector.size()); - // for the TextLayouterDevice, it is necessary to have a scaling representing - // the font size. Since we want to extract polygons here, it is okay to - // work just with scaling and to ignore shear, rotation and translation, - // all that can be applied to the polygons later - const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale)); + if (!nCount) + return nullptr; - // prepare textlayoutdevice - TextLayouterDevice aTextLayouter; - aTextLayouter.setFontAttribute( - getFontAttribute(), - aFontScale.getX(), - aFontScale.getY(), - getLocale()); - - // When getting outlines from stretched text (aScale.getX() != 1.0) it - // is necessary to inverse-scale the DXArray (if used) to not get the - // outlines already aligned to given, but wrong DXArray - if(!getDXArray().empty() && !basegfx::fTools::equal(aScale.getX(), 1.0)) - { - std::vector< double > aScaledDXArray = getDXArray(); - const double fDXArrayScale(1.0 / aScale.getX()); - - for(double & a : aScaledDXArray) - { - a *= fDXArrayScale; - } - - // get the text outlines - aTextLayouter.getTextOutlines( - rTarget, - getText(), - getTextPosition(), - getTextLength(), - aScaledDXArray); - } - else - { - // get the text outlines - aTextLayouter.getTextOutlines( - rTarget, - getText(), - getTextPosition(), - getTextLength(), - getDXArray()); - } + // alloc space for the primitives + Primitive2DContainer aRetval; + aRetval.resize(nCount); - // create primitives for the outlines - const sal_uInt32 nCount(rTarget.size()); + // color-filled polypolygons + for (sal_uInt32 a(0); a < nCount; a++) + { + // prepare polypolygon + basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a]; + rPolyPolygon.transform(aPolygonTransform); + aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor()); + } - if(nCount) - { - // prepare object transformation for polygons - rTransformation = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( - aScale, fShearX, fRotate, aTranslate); - } - } + if (getFontAttribute().getOutline()) + { + // decompose polygon transformation to single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // create outline text effect with current content and replace + return new TextEffectPrimitive2D(std::move(aRetval), aTranslate, fRotate, + TextEffectStyle2D::Outline); + } - void TextSimplePortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const - { - if(!getTextLength()) - return; + return new GroupPrimitive2D(std::move(aRetval)); +} + +TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D( + basegfx::B2DHomMatrix rNewTransform, OUString rText, sal_Int32 nTextPosition, + sal_Int32 nTextLength, std::vector<double>&& rDXArray, std::vector<sal_Bool>&& rKashidaArray, + attribute::FontAttribute aFontAttribute, css::lang::Locale aLocale, + const basegfx::BColor& rFontColor, const Color& rTextFillColor) + : maTextTransform(std::move(rNewTransform)) + , maText(std::move(rText)) + , mnTextPosition(nTextPosition) + , mnTextLength(nTextLength) + , maDXArray(std::move(rDXArray)) + , maKashidaArray(std::move(rKashidaArray)) + , maFontAttribute(std::move(aFontAttribute)) + , maLocale(std::move(aLocale)) + , maFontColor(rFontColor) + , maTextFillColor(rTextFillColor) +{ +#if OSL_DEBUG_LEVEL > 0 + const sal_Int32 aStringLength(getText().getLength()); + OSL_ENSURE(aStringLength >= getTextPosition() + && aStringLength >= getTextPosition() + getTextLength(), + "TextSimplePortionPrimitive2D with text out of range (!)"); +#endif +} - Primitive2DContainer aRetval; - basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; - basegfx::B2DHomMatrix aPolygonTransform; +bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB) +{ + return (rA.Language == rB.Language && rA.Country == rB.Country && rA.Variant == rB.Variant); +} - // get text outlines and their object transformation - getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform); +bool TextSimplePortionPrimitive2D::hasTextRelief() const +{ + // not possible for TextSimplePortionPrimitive2D + return false; +} - // create primitives for the outlines - const sal_uInt32 nCount(aB2DPolyPolyVector.size()); +bool TextSimplePortionPrimitive2D::hasShadow() const +{ + // not possible for TextSimplePortionPrimitive2D + return false; +} - if(!nCount) - return; +bool TextSimplePortionPrimitive2D::hasTextDecoration() const +{ + // not possible for TextSimplePortionPrimitive2D + return false; +} - // alloc space for the primitives - aRetval.resize(nCount); +bool TextSimplePortionPrimitive2D::hasOutline() const +{ + // not allowed with TextRelief, else defined in FontAttributes + return !hasTextRelief() && getFontAttribute().getOutline(); +} - // color-filled polypolygons - for(sal_uInt32 a(0); a < nCount; a++) - { - // prepare polypolygon - basegfx::B2DPolyPolygon& rPolyPolygon = aB2DPolyPolyVector[a]; - rPolyPolygon.transform(aPolygonTransform); - aRetval[a] = new PolyPolygonColorPrimitive2D(rPolyPolygon, getFontColor()); - } +bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const TextSimplePortionPrimitive2D& rCompare + = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive); + + return (getTextTransform() == rCompare.getTextTransform() && getText() == rCompare.getText() + && getTextPosition() == rCompare.getTextPosition() + && getTextLength() == rCompare.getTextLength() + && getDXArray() == rCompare.getDXArray() + && getKashidaArray() == rCompare.getKashidaArray() + && getFontAttribute() == rCompare.getFontAttribute() + && LocalesAreEqual(getLocale(), rCompare.getLocale()) + && getFontColor() == rCompare.getFontColor() + && maTextFillColor == rCompare.maTextFillColor); + } - if(getFontAttribute().getOutline()) - { - // decompose polygon transformation to single values - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - aPolygonTransform.decompose(aScale, aTranslate, fRotate, fShearX); - - // create outline text effect with current content and replace - Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D( - aRetval, - aTranslate, - fRotate, - TextEffectStyle2D::Outline)); - - aRetval = Primitive2DContainer { aNewTextEffect }; - } + return false; +} - rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end()); - } +basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + if (maB2DRange.isEmpty() && getTextLength()) + { + // get TextBoundRect as base size + // decompose object transformation to single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; - TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D( - const basegfx::B2DHomMatrix& rNewTransform, - const OUString& rText, - sal_Int32 nTextPosition, - sal_Int32 nTextLength, - const std::vector< double >& rDXArray, - const attribute::FontAttribute& rFontAttribute, - const css::lang::Locale& rLocale, - const basegfx::BColor& rFontColor, - bool bFilled, - long nWidthToFill, - const Color& rTextFillColor) - : BufferedDecompositionPrimitive2D(), - maTextTransform(rNewTransform), - maText(rText), - mnTextPosition(nTextPosition), - mnTextLength(nTextLength), - maDXArray(rDXArray), - maFontAttribute(rFontAttribute), - maLocale(rLocale), - maFontColor(rFontColor), - mbFilled(bFilled), - mnWidthToFill(nWidthToFill), - maTextFillColor(rTextFillColor), - maB2DRange() + if (getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)) { -#if OSL_DEBUG_LEVEL > 0 - const sal_Int32 aStringLength(getText().getLength()); - OSL_ENSURE(aStringLength >= getTextPosition() && aStringLength >= getTextPosition() + getTextLength(), - "TextSimplePortionPrimitive2D with text out of range (!)"); -#endif - } + // for the TextLayouterDevice, it is necessary to have a scaling representing + // the font size. Since we want to extract polygons here, it is okay to + // work just with scaling and to ignore shear, rotation and translation, + // all that can be applied to the polygons later + const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale)); - bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB) - { - return (rA.Language == rB.Language - && rA.Country == rB.Country - && rA.Variant == rB.Variant); - } + // prepare textlayoutdevice + TextLayouterDevice aTextLayouter; + aTextLayouter.setFontAttribute(getFontAttribute(), aFontScale.getX(), aFontScale.getY(), + getLocale()); - bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const - { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + // get basic text range + basegfx::B2DRange aNewRange( + aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength())); + + // #i104432#, #i102556# take empty results into account + if (!aNewRange.isEmpty()) { - const TextSimplePortionPrimitive2D& rCompare = static_cast<const TextSimplePortionPrimitive2D&>(rPrimitive); - - return (getTextTransform() == rCompare.getTextTransform() - && getText() == rCompare.getText() - && getTextPosition() == rCompare.getTextPosition() - && getTextLength() == rCompare.getTextLength() - && getDXArray() == rCompare.getDXArray() - && getFontAttribute() == rCompare.getFontAttribute() - && LocalesAreEqual(getLocale(), rCompare.getLocale()) - && getFontColor() == rCompare.getFontColor() - && mbFilled == rCompare.mbFilled - && mnWidthToFill == rCompare.mnWidthToFill - && maTextFillColor == rCompare.maTextFillColor); - } + // prepare object transformation for range + const basegfx::B2DHomMatrix aRangeTransformation( + basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, fShearX, fRotate, aTranslate)); - return false; - } + // apply range transformation to it + aNewRange.transform(aRangeTransformation); - basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const - { - if(maB2DRange.isEmpty() && getTextLength()) - { - // get TextBoundRect as base size - // decompose object transformation to single values - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - - if(getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)) - { - // for the TextLayouterDevice, it is necessary to have a scaling representing - // the font size. Since we want to extract polygons here, it is okay to - // work just with scaling and to ignore shear, rotation and translation, - // all that can be applied to the polygons later - const basegfx::B2DVector aFontScale(getCorrectedScaleAndFontScale(aScale)); - - // prepare textlayoutdevice - TextLayouterDevice aTextLayouter; - aTextLayouter.setFontAttribute( - getFontAttribute(), - aFontScale.getX(), - aFontScale.getY(), - getLocale()); - - // get basic text range - basegfx::B2DRange aNewRange(aTextLayouter.getTextBoundRect(getText(), getTextPosition(), getTextLength())); - - // #i104432#, #i102556# take empty results into account - if(!aNewRange.isEmpty()) - { - // prepare object transformation for range - const basegfx::B2DHomMatrix aRangeTransformation(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( - aScale, fShearX, fRotate, aTranslate)); - - // apply range transformation to it - aNewRange.transform(aRangeTransformation); - - // assign to buffered value - const_cast< TextSimplePortionPrimitive2D* >(this)->maB2DRange = aNewRange; - } - } + // assign to buffered value + const_cast<TextSimplePortionPrimitive2D*>(this)->maB2DRange = aNewRange; } - - return maB2DRange; } + } - // provide unique ID - ImplPrimitive2DIDBlock(TextSimplePortionPrimitive2D, PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D) + return maB2DRange; +} + +void TextSimplePortionPrimitive2D::createTextLayouter(TextLayouterDevice& rTextLayouter) const +{ + // decompose primitive-local matrix to get local font scaling + const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform()); + + // create a TextLayouter to access encapsulated VCL Text/Font related tooling + rTextLayouter.setFontAttribute(getFontAttribute(), aDecTrans.getScale().getX(), + aDecTrans.getScale().getY(), getLocale()); + + if (getFontAttribute().getRTL()) + { + vcl::text::ComplexTextLayoutFlags nRTLLayoutMode( + rTextLayouter.getLayoutMode() & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong); + nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl + | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; + rTextLayouter.setLayoutMode(nRTLLayoutMode); + } + else + { + // tdf#101686: This is LTR text, but the output device may have RTL state. + vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(rTextLayouter.getLayoutMode()); + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl; + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong; + rTextLayouter.setLayoutMode(nLTRLayoutMode); + } +} + +std::unique_ptr<SalLayout> +TextSimplePortionPrimitive2D::createSalLayout(const TextLayouterDevice& rTextLayouter) const +{ + // As mentioned above we can act in the + // Text's local coordinate system without transformation at all + const ::std::vector<double>& rDXArray(getDXArray()); + + // create SalLayout. No need for a position, as mentioned text can work + // without transformations, so start point is always 0,0 + return rTextLayouter.getSalLayout(getText(), getTextPosition(), getTextLength(), + basegfx::B2DPoint(0.0, 0.0), rDXArray, getKashidaArray()); +} + +// provide unique ID +sal_uInt32 TextSimplePortionPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D; +} } // end of namespace diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx index 23b0c5ffe40c..497f8c2dbe40 100644 --- a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx @@ -24,19 +24,20 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <drawinglayer/attribute/lineattribute.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <osl/diagnose.h> #include <rtl/ustrbuf.hxx> +#include <utility> namespace drawinglayer::primitive2d { BaseTextStrikeoutPrimitive2D::BaseTextStrikeoutPrimitive2D( - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, double fWidth, const basegfx::BColor& rFontColor) - : BufferedDecompositionPrimitive2D(), - maObjectTransformation(rObjectTransformation), + : maObjectTransformation(std::move(aObjectTransformation)), mfWidth(fWidth), maFontColor(rFontColor) { @@ -57,7 +58,7 @@ namespace drawinglayer::primitive2d } - void TextCharacterStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference TextCharacterStrikeoutPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { // strikeout with character const OUString aSingleCharString(getStrikeoutChar()); @@ -89,16 +90,17 @@ namespace drawinglayer::primitive2d } auto len = aStrikeoutString.getLength(); - rContainer.push_back( + return new TextSimplePortionPrimitive2D( getObjectTransformation(), aStrikeoutString.makeStringAndClear(), 0, len, - aDXArray, + std::move(aDXArray), + {}, getFontAttribute(), getLocale(), - getFontColor())); + getFontColor()); } TextCharacterStrikeoutPrimitive2D::TextCharacterStrikeoutPrimitive2D( @@ -106,12 +108,12 @@ namespace drawinglayer::primitive2d double fWidth, const basegfx::BColor& rFontColor, sal_Unicode aStrikeoutChar, - const attribute::FontAttribute& rFontAttribute, - const css::lang::Locale& rLocale) + attribute::FontAttribute aFontAttribute, + css::lang::Locale aLocale) : BaseTextStrikeoutPrimitive2D(rObjectTransformation, fWidth, rFontColor), maStrikeoutChar(aStrikeoutChar), - maFontAttribute(rFontAttribute), - maLocale(rLocale) + maFontAttribute(std::move(aFontAttribute)), + maLocale(std::move(aLocale)) { } @@ -130,11 +132,12 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextCharacterStrikeoutPrimitive2D, PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D) - - + sal_uInt32 TextCharacterStrikeoutPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTCHARACTERSTRIKEOUTPRIMITIVE2D; + } - void TextGeometryStrikeoutPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference TextGeometryStrikeoutPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { OSL_ENSURE(TEXT_STRIKEOUT_SLASH != getTextStrikeout() && TEXT_STRIKEOUT_X != getTextStrikeout(), "Wrong TEXT_STRIKEOUT type; a TextCharacterStrikeoutPrimitive2D should be used (!)"); @@ -188,8 +191,7 @@ namespace drawinglayer::primitive2d // add primitive const attribute::LineAttribute aLineAttribute(getFontColor(), fStrikeoutHeight, basegfx::B2DLineJoin::NONE); - Primitive2DContainer xRetval(1); - xRetval[0] = new PolygonStrokePrimitive2D(aStrikeoutLine, aLineAttribute); + Primitive2DReference xRetval = new PolygonStrokePrimitive2D(std::move(aStrikeoutLine), aLineAttribute); if(bDoubleLine) { @@ -209,14 +211,14 @@ namespace drawinglayer::primitive2d aTransform.rotate(fRotate); aTransform.translate(aTranslate.getX(), aTranslate.getY()); - // add transform primitive - xRetval.push_back( - new TransformPrimitive2D( - aTransform, - xRetval)); + // add original and transform primitive to a GroupPrimitive2D + xRetval = new GroupPrimitive2D({ + xRetval, new TransformPrimitive2D( + aTransform, + Primitive2DContainer{xRetval}) }); } - rContainer.insert(rContainer.end(), xRetval.begin(), xRetval.end()); + return xRetval; } TextGeometryStrikeoutPrimitive2D::TextGeometryStrikeoutPrimitive2D( @@ -248,7 +250,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TextGeometryStrikeoutPrimitive2D, PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D) + sal_uInt32 TextGeometryStrikeoutPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TEXTGEOMETRYSTRIKEOUTPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/transformprimitive2d.cxx b/drawinglayer/source/primitive2d/transformprimitive2d.cxx index 4ac2ada55027..8c36fa9a73b9 100644 --- a/drawinglayer/source/primitive2d/transformprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/transformprimitive2d.cxx @@ -19,6 +19,8 @@ #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/Tools.hxx> +#include <utility> using namespace com::sun::star; @@ -27,20 +29,29 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { TransformPrimitive2D::TransformPrimitive2D( - const basegfx::B2DHomMatrix& rTransformation, - const Primitive2DContainer& rChildren) - : GroupPrimitive2D(rChildren), - maTransformation(rTransformation) + basegfx::B2DHomMatrix aTransformation, + Primitive2DContainer&& aChildren) + : maTransformation(std::move(aTransformation)), + mxChildren(new GroupPrimitive2D(std::move(aChildren))) + { + } + + TransformPrimitive2D::TransformPrimitive2D( + basegfx::B2DHomMatrix aTransformation, + GroupPrimitive2D& rChildren) + : maTransformation(std::move(aTransformation)), + mxChildren(&rChildren) { } bool TransformPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(GroupPrimitive2D::operator==(rPrimitive)) + if(BasePrimitive2D::operator==(rPrimitive)) { const TransformPrimitive2D& rCompare = static_cast< const TransformPrimitive2D& >(rPrimitive); - return (getTransformation() == rCompare.getTransformation()); + return maTransformation == rCompare.maTransformation + && arePrimitive2DReferencesEqual(mxChildren, rCompare.mxChildren); } return false; @@ -54,7 +65,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TransformPrimitive2D, PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D) + sal_uInt32 TransformPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx index da157d353209..8a86b1b295f4 100644 --- a/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/transparenceprimitive2d.cxx @@ -27,10 +27,10 @@ using namespace com::sun::star; namespace drawinglayer::primitive2d { TransparencePrimitive2D::TransparencePrimitive2D( - const Primitive2DContainer& rChildren, - const Primitive2DContainer& rTransparence) - : GroupPrimitive2D(rChildren), - maTransparence(rTransparence) + Primitive2DContainer&& aChildren, + Primitive2DContainer&& aTransparence) + : GroupPrimitive2D(std::move(aChildren)), + maTransparence(std::move(aTransparence)) { } @@ -47,7 +47,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(TransparencePrimitive2D, PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D) + sal_uInt32 TransparencePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx index 70cfe675046e..5ecd367b1c95 100644 --- a/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/unifiedtransparenceprimitive2d.cxx @@ -21,21 +21,18 @@ #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/color/bcolor.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> - - -using namespace com::sun::star; namespace drawinglayer::primitive2d { UnifiedTransparencePrimitive2D::UnifiedTransparencePrimitive2D( - const Primitive2DContainer& rChildren, + Primitive2DContainer&& aChildren, double fTransparence) - : GroupPrimitive2D(rChildren), + : GroupPrimitive2D(std::move(aChildren)), mfTransparence(fTransparence) { } @@ -85,15 +82,15 @@ namespace drawinglayer::primitive2d // I will take the last one here. The small overhead of two primitives will only be // used when UnifiedTransparencePrimitive2D is not handled directly. const basegfx::B2DRange aPolygonRange(getChildren().getB2DRange(rViewInformation)); - const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aPolygonRange)); + basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aPolygonRange)); const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence()); Primitive2DContainer aTransparenceContent(2); - aTransparenceContent[0] = Primitive2DReference(new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), aGray)); - aTransparenceContent[1] = Primitive2DReference(new PolygonHairlinePrimitive2D(aPolygon, aGray)); + aTransparenceContent[0] = new PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), aGray); + aTransparenceContent[1] = new PolygonHairlinePrimitive2D(std::move(aPolygon), aGray); // create sub-transparence group with a gray-colored rectangular fill polygon - rVisitor.append(new TransparencePrimitive2D(getChildren(), aTransparenceContent)); + rVisitor.visit(new TransparencePrimitive2D(Primitive2DContainer(getChildren()), std::move(aTransparenceContent))); } else { @@ -102,7 +99,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(UnifiedTransparencePrimitive2D, PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D) + sal_uInt32 UnifiedTransparencePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx index d8926824f65a..ba4f06341f4a 100644 --- a/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/wallpaperprimitive2d.cxx @@ -31,199 +31,194 @@ namespace drawinglayer::primitive2d { - void WallpaperBitmapPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference WallpaperBitmapPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { + + if(getLocalObjectRange().isEmpty() || getBitmap().IsEmpty()) + return nullptr; + Primitive2DReference aRetval; - if(!getLocalObjectRange().isEmpty() && !getBitmapEx().IsEmpty()) - { - // get bitmap PIXEL size - const Size& rPixelSize = getBitmapEx().GetSizePixel(); + // get bitmap PIXEL size + const Size aPixelSize = getBitmap().GetSizePixel(); - if(rPixelSize.Width() > 0 && rPixelSize.Height() > 0) + if(aPixelSize.Width() <= 0 || aPixelSize.Height() <= 0) + return nullptr; + + if(WallpaperStyle::Scale == getWallpaperStyle()) + { + // shortcut for scale; use simple BitmapPrimitive2D + basegfx::B2DHomMatrix aObjectTransform; + + aObjectTransform.set(0, 0, getLocalObjectRange().getWidth()); + aObjectTransform.set(1, 1, getLocalObjectRange().getHeight()); + aObjectTransform.set(0, 2, getLocalObjectRange().getMinX()); + aObjectTransform.set(1, 2, getLocalObjectRange().getMinY()); + + aRetval.set( + new BitmapPrimitive2D( + getBitmap(), + aObjectTransform)); + } + else + { + // transform to logic size + basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation()); + aInverseViewTransformation.invert(); + basegfx::B2DVector aLogicSize(aPixelSize.Width(), aPixelSize.Height()); + aLogicSize = aInverseViewTransformation * aLogicSize; + + // apply layout + basegfx::B2DPoint aTargetTopLeft(getLocalObjectRange().getMinimum()); + bool bUseTargetTopLeft(true); + bool bNeedsClipping(false); + + switch(getWallpaperStyle()) { - if(WallpaperStyle::Scale == getWallpaperStyle()) + default: //case WallpaperStyle::Tile :, also WallpaperStyle::NONE and WallpaperStyle::ApplicationGradient + { + bUseTargetTopLeft = false; + break; + } + case WallpaperStyle::Scale : + { + // handled by shortcut above + break; + } + case WallpaperStyle::TopLeft : + { + // nothing to do + break; + } + case WallpaperStyle::Top : + { + const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); + aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5)); + break; + } + case WallpaperStyle::TopRight : + { + aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX()); + break; + } + case WallpaperStyle::Left : + { + const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); + aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5)); + break; + } + case WallpaperStyle::Center : + { + const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); + aTargetTopLeft = aCenter - (aLogicSize * 0.5); + break; + } + case WallpaperStyle::Right : { - // shortcut for scale; use simple BitmapPrimitive2D - basegfx::B2DHomMatrix aObjectTransform; + const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); + aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX()); + aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5)); + break; + } + case WallpaperStyle::BottomLeft : + { + aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY()); + break; + } + case WallpaperStyle::Bottom : + { + const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); + aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5)); + aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY()); + break; + } + case WallpaperStyle::BottomRight : + { + aTargetTopLeft = getLocalObjectRange().getMaximum() - aLogicSize; + break; + } + } - aObjectTransform.set(0, 0, getLocalObjectRange().getWidth()); - aObjectTransform.set(1, 1, getLocalObjectRange().getHeight()); - aObjectTransform.set(0, 2, getLocalObjectRange().getMinX()); - aObjectTransform.set(1, 2, getLocalObjectRange().getMinY()); + if(bUseTargetTopLeft) + { + // fill target range + const basegfx::B2DRange aTargetRange(aTargetTopLeft, aTargetTopLeft + aLogicSize); - Primitive2DReference xReference( - new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()), - aObjectTransform)); + // create aligned, single BitmapPrimitive2D + basegfx::B2DHomMatrix aObjectTransform; - aRetval = xReference; - } - else + aObjectTransform.set(0, 0, aTargetRange.getWidth()); + aObjectTransform.set(1, 1, aTargetRange.getHeight()); + aObjectTransform.set(0, 2, aTargetRange.getMinX()); + aObjectTransform.set(1, 2, aTargetRange.getMinY()); + + aRetval.set( + new BitmapPrimitive2D( + getBitmap(), + aObjectTransform)); + + // clip when not completely inside object range + bNeedsClipping = !getLocalObjectRange().isInside(aTargetRange); + } + else + { + // WallpaperStyle::Tile, WallpaperStyle::NONE, WallpaperStyle::ApplicationGradient + // convert to relative positions + const basegfx::B2DVector aRelativeSize( + aLogicSize.getX() / (getLocalObjectRange().getWidth() ? getLocalObjectRange().getWidth() : 1.0), + aLogicSize.getY() / (getLocalObjectRange().getHeight() ? getLocalObjectRange().getHeight() : 1.0)); + basegfx::B2DPoint aRelativeTopLeft(0.0, 0.0); + + if(WallpaperStyle::Tile != getWallpaperStyle()) { - // transform to logic size - basegfx::B2DHomMatrix aInverseViewTransformation(getViewTransformation()); - aInverseViewTransformation.invert(); - basegfx::B2DVector aLogicSize(rPixelSize.Width(), rPixelSize.Height()); - aLogicSize = aInverseViewTransformation * aLogicSize; - - // apply layout - basegfx::B2DPoint aTargetTopLeft(getLocalObjectRange().getMinimum()); - bool bUseTargetTopLeft(true); - bool bNeedsClipping(false); - - switch(getWallpaperStyle()) - { - default: //case WallpaperStyle::Tile :, also WallpaperStyle::NONE and WallpaperStyle::ApplicationGradient - { - bUseTargetTopLeft = false; - break; - } - case WallpaperStyle::Scale : - { - // handled by shortcut above - break; - } - case WallpaperStyle::TopLeft : - { - // nothing to do - break; - } - case WallpaperStyle::Top : - { - const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); - aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5)); - break; - } - case WallpaperStyle::TopRight : - { - aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX()); - break; - } - case WallpaperStyle::Left : - { - const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); - aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5)); - break; - } - case WallpaperStyle::Center : - { - const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); - aTargetTopLeft = aCenter - (aLogicSize * 0.5); - break; - } - case WallpaperStyle::Right : - { - const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); - aTargetTopLeft.setX(getLocalObjectRange().getMaxX() - aLogicSize.getX()); - aTargetTopLeft.setY(aCenter.getY() - (aLogicSize.getY() * 0.5)); - break; - } - case WallpaperStyle::BottomLeft : - { - aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY()); - break; - } - case WallpaperStyle::Bottom : - { - const basegfx::B2DPoint aCenter(getLocalObjectRange().getCenter()); - aTargetTopLeft.setX(aCenter.getX() - (aLogicSize.getX() * 0.5)); - aTargetTopLeft.setY(getLocalObjectRange().getMaxY() - aLogicSize.getY()); - break; - } - case WallpaperStyle::BottomRight : - { - aTargetTopLeft = getLocalObjectRange().getMaximum() - aLogicSize; - break; - } - } - - if(bUseTargetTopLeft) - { - // fill target range - const basegfx::B2DRange aTargetRange(aTargetTopLeft, aTargetTopLeft + aLogicSize); - - // create aligned, single BitmapPrimitive2D - basegfx::B2DHomMatrix aObjectTransform; - - aObjectTransform.set(0, 0, aTargetRange.getWidth()); - aObjectTransform.set(1, 1, aTargetRange.getHeight()); - aObjectTransform.set(0, 2, aTargetRange.getMinX()); - aObjectTransform.set(1, 2, aTargetRange.getMinY()); - - Primitive2DReference xReference( - new BitmapPrimitive2D( - VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()), - aObjectTransform)); - aRetval = xReference; - - // clip when not completely inside object range - bNeedsClipping = !getLocalObjectRange().isInside(aTargetRange); - } - else - { - // WallpaperStyle::Tile, WallpaperStyle::NONE, WallpaperStyle::ApplicationGradient - // convert to relative positions - const basegfx::B2DVector aRelativeSize( - aLogicSize.getX() / (getLocalObjectRange().getWidth() ? getLocalObjectRange().getWidth() : 1.0), - aLogicSize.getY() / (getLocalObjectRange().getHeight() ? getLocalObjectRange().getHeight() : 1.0)); - basegfx::B2DPoint aRelativeTopLeft(0.0, 0.0); - - if(WallpaperStyle::Tile != getWallpaperStyle()) - { - aRelativeTopLeft.setX(0.5 - aRelativeSize.getX()); - aRelativeTopLeft.setY(0.5 - aRelativeSize.getY()); - } - - // prepare FillGraphicAttribute - const attribute::FillGraphicAttribute aFillGraphicAttribute( - Graphic(getBitmapEx()), - basegfx::B2DRange(aRelativeTopLeft, aRelativeTopLeft+ aRelativeSize), - true); - - // create ObjectTransform - const basegfx::B2DHomMatrix aObjectTransform( - basegfx::utils::createScaleTranslateB2DHomMatrix( - getLocalObjectRange().getRange(), - getLocalObjectRange().getMinimum())); - - // create FillBitmapPrimitive - const drawinglayer::primitive2d::Primitive2DReference xFillBitmap( - new drawinglayer::primitive2d::FillGraphicPrimitive2D( - aObjectTransform, - aFillGraphicAttribute)); - aRetval = xFillBitmap; - - // always embed tiled fill to clipping - bNeedsClipping = true; - } - - if(bNeedsClipping) - { - // embed to clipping; this is necessary for tiled fills - const basegfx::B2DPolyPolygon aPolyPolygon( - basegfx::utils::createPolygonFromRect(getLocalObjectRange())); - const drawinglayer::primitive2d::Primitive2DReference xClippedFill( - new drawinglayer::primitive2d::MaskPrimitive2D( - aPolyPolygon, - { aRetval })); - aRetval = xClippedFill; - } + aRelativeTopLeft.setX(0.5 - aRelativeSize.getX()); + aRelativeTopLeft.setY(0.5 - aRelativeSize.getY()); } + + // prepare FillGraphicAttribute + const attribute::FillGraphicAttribute aFillGraphicAttribute( + Graphic(getBitmap()), + basegfx::B2DRange(aRelativeTopLeft, aRelativeTopLeft+ aRelativeSize), + true); + + // create ObjectTransform + const basegfx::B2DHomMatrix aObjectTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix( + getLocalObjectRange().getRange(), + getLocalObjectRange().getMinimum())); + + // create FillBitmapPrimitive + aRetval.set( + new drawinglayer::primitive2d::FillGraphicPrimitive2D( + aObjectTransform, + aFillGraphicAttribute)); + + // always embed tiled fill to clipping + bNeedsClipping = true; + } + + if(bNeedsClipping) + { + // embed to clipping; this is necessary for tiled fills + basegfx::B2DPolyPolygon aPolyPolygon( + basegfx::utils::createPolygonFromRect(getLocalObjectRange())); + const drawinglayer::primitive2d::Primitive2DReference xClippedFill( + new drawinglayer::primitive2d::MaskPrimitive2D( + std::move(aPolyPolygon), + { aRetval })); + aRetval = xClippedFill; } } - if (aRetval.is()) - rContainer.push_back(aRetval); + return aRetval; } WallpaperBitmapPrimitive2D::WallpaperBitmapPrimitive2D( const basegfx::B2DRange& rObjectRange, - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, WallpaperStyle eWallpaperStyle) - : ViewTransformationDependentPrimitive2D(), - maObjectRange(rObjectRange), - maBitmapEx(rBitmapEx), + : maObjectRange(rObjectRange), + maBitmap(rBitmap), meWallpaperStyle(eWallpaperStyle) { } @@ -235,7 +230,7 @@ namespace drawinglayer::primitive2d const WallpaperBitmapPrimitive2D& rCompare = static_cast<const WallpaperBitmapPrimitive2D&>(rPrimitive); return (getLocalObjectRange() == rCompare.getLocalObjectRange() - && getBitmapEx() == rCompare.getBitmapEx() + && getBitmap() == rCompare.getBitmap() && getWallpaperStyle() == rCompare.getWallpaperStyle()); } @@ -248,7 +243,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(WallpaperBitmapPrimitive2D, PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D) + sal_uInt32 WallpaperBitmapPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_WALLPAPERBITMAPPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx index f0eb918339bd..1efc7442ba73 100644 --- a/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/wrongspellprimitive2d.cxx @@ -19,61 +19,115 @@ #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> #include <basegfx/polygon/b2dpolygon.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <utility> namespace drawinglayer::primitive2d { - void WrongSpellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + Primitive2DReference WrongSpellPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const { - // ATM this decompose is view-independent, what the original VCL-Display is not. To mimic - // the old behaviour here if wanted it is necessary to add get2DDecomposition and implement - // it similar to the usage in e.g. HelplinePrimitive2D. Remembering the ViewTransformation - // should be enough then. - // The view-independent wavelines work well (if You ask me). Maybe the old VCL-Behaviour is only - // in place because it was not possible/too expensive at that time to scale the wavelines with the - // view... - // With the VCL-PixelRenderer this will not even be used since it implements WrongSpellPrimitive2D - // directly and mimics the old VCL-Display there. If You implemented a new renderer without - // direct WrongSpellPrimitive2D support, You may want to do the described change here. + // This *was* a view-independent primitive before (see before this commit), + // but was never really used, but handled in various processors anyways. + // Since the current VCL primitive renderer and it's functions used in + // VCL do render this always in one discrete pixel size I decided to + // adapt this primitive to do that, too, but - due to being a primitive - + // with the correct invalidate/hit-ranges etc. + // I use here DiscreteMetricDependentPrimitive2D which already implements + // buffering and providing the discrete size using 'getDiscreteUnit()' plus + // the needed updates to buffering, what makes the rest simple. + // NOTE: If one day the (in my opinion) also good looking view-independent + // version should be needed again, just revert this change + if (basegfx::fTools::lessOrEqual(getStop(), getStart())) + { + // stop smaller or equal to start, done + return nullptr; + } // get the font height (part of scale), so decompose the matrix basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX; getTransformation().decompose(aScale, aTranslate, fRotate, fShearX); + constexpr double constMinimumFontHeight(5.0); + if (aScale.getY() / getDiscreteUnit() < constMinimumFontHeight) + { + // font height smaller constMinimumFontHeight pixels -> decompose to empty + return nullptr; + } + // calculate distances based on a static default (to allow testing in debugger) static const double fDefaultDistance(0.03); const double fFontHeight(aScale.getY()); const double fUnderlineDistance(fFontHeight * fDefaultDistance); - const double fWaveWidth(2.0 * fUnderlineDistance); // the Y-distance needs to be relative to FontHeight since the points get // transformed with the transformation containing that scale already. const double fRelativeUnderlineDistance(basegfx::fTools::equalZero(aScale.getY()) ? 0.0 : fUnderlineDistance / aScale.getY()); - basegfx::B2DPoint aStart(getStart(), fRelativeUnderlineDistance); - basegfx::B2DPoint aStop(getStop(), fRelativeUnderlineDistance); - basegfx::B2DPolygon aPolygon; - aPolygon.append(getTransformation() * aStart); - aPolygon.append(getTransformation() * aStop); + // get start/stop positions and WaveLength, base all calculations for discrete + // waveline on these initial values + basegfx::B2DPoint aStart(getTransformation() * basegfx::B2DPoint(getStart(), fRelativeUnderlineDistance)); + const basegfx::B2DPoint aStop(getTransformation() * basegfx::B2DPoint(getStop(), fRelativeUnderlineDistance)); + const double fWaveLength(getDiscreteUnit() * 8); + + // get pre-calculated vector and controlPoint for one wave segment + basegfx::B2DVector aVector(aStop - aStart); + double fLength(aVector.getLength()); + aVector.normalize(); + basegfx::B2DVector aControl(basegfx::getPerpendicular(aVector)); + aVector *= fWaveLength; + aControl = aControl * (fWaveLength * 0.5) + aVector * 0.5; + + // create geometry + basegfx::B2DPolygon aWave; + aWave.append(aStart); + + while (fLength > fWaveLength) + { + // one WaveSegment per WaveLength + basegfx::B2DPoint aNew(aStart + aVector); + aWave.appendBezierSegment( + aStart + aControl, + aNew - aControl, + aNew); + aStart = aNew; + fLength -= fWaveLength; + } - // prepare line attribute - const attribute::LineAttribute aLineAttribute(getColor()); + if (fLength > fWaveLength * 0.2) + { + // if rest is more than 20% of WaveLength, create + // remaining snippet and add it + basegfx::B2DPoint aNew(aStart + aVector); + basegfx::B2DCubicBezier aRest( + aStart, + aStart + aControl, + aNew - aControl, + aNew); + aRest = aRest.snippet(0.0, fLength/fWaveLength); + aWave.appendBezierSegment( + aRest.getControlPointA(), + aRest.getControlPointB(), + aRest.getEndPoint()); + } - // create the waveline primitive - rContainer.push_back(new PolygonWavePrimitive2D(aPolygon, aLineAttribute, fWaveWidth, 0.5 * fWaveWidth)); + // create & return primitive + return new PolygonHairlinePrimitive2D( + std::move(aWave), + getColor()); } WrongSpellPrimitive2D::WrongSpellPrimitive2D( - const basegfx::B2DHomMatrix& rTransformation, + basegfx::B2DHomMatrix aTransformation, double fStart, double fStop, const basegfx::BColor& rColor) - : BufferedDecompositionPrimitive2D(), - maTransformation(rTransformation), + : DiscreteMetricDependentPrimitive2D(), + maTransformation(std::move(aTransformation)), mfStart(fStart), mfStop(fStop), maColor(rColor) @@ -82,7 +136,7 @@ namespace drawinglayer::primitive2d bool WrongSpellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const { - if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) { const WrongSpellPrimitive2D& rCompare = static_cast<const WrongSpellPrimitive2D&>(rPrimitive); @@ -96,7 +150,10 @@ namespace drawinglayer::primitive2d } // provide unique ID - ImplPrimitive2DIDBlock(WrongSpellPrimitive2D, PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D) + sal_uInt32 WrongSpellPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D; + } } // end of namespace diff --git a/drawinglayer/source/primitive3d/Tools.cxx b/drawinglayer/source/primitive3d/Tools.cxx new file mode 100644 index 000000000000..23ecc0851f40 --- /dev/null +++ b/drawinglayer/source/primitive3d/Tools.cxx @@ -0,0 +1,67 @@ +/* -*- 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 <drawinglayer/primitive3d/Tools.hxx> +#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> + +using namespace css; + +namespace drawinglayer::primitive3d +{ +OUString idToString(sal_uInt32 nId) +{ + switch (nId) + { + case PRIMITIVE3D_ID_GROUPPRIMITIVE3D: + return u"GROUPPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_HATCHTEXTUREPRIMITIVE3D: + return u"HATCHTEXTUREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D: + return u"MODIFIEDCOLORPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D: + return u"POLYGONHAIRLINEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_POLYGONSTROKEPRIMITIVE3D: + return u"POLYGONSTROKEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_POLYGONTUBEPRIMITIVE3D: + return u"POLYGONTUBEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D: + return u"POLYPOLYGONMATERIALPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SDRCUBEPRIMITIVE3D: + return u"SDRCUBEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D: + return u"SDREXTRUDEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SDRLATHEPRIMITIVE3D: + return u"SDRLATHEPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SDRPOLYPOLYGONPRIMITIVE3D: + return u"SDRPOLYPOLYGONPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SDRSPHEREPRIMITIVE3D: + return u"SDRSPHEREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_SHADOWPRIMITIVE3D: + return u"SHADOWPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D: + return u"UNIFIEDTRANSPARENCETEXTUREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_GRADIENTTEXTUREPRIMITIVE3D: + return u"GRADIENTTEXTUREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_BITMAPTEXTUREPRIMITIVE3D: + return u"BITMAPTEXTUREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_TRANSPARENCETEXTUREPRIMITIVE3D: + return u"TRANSPARENCETEXTUREPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D: + return u"TRANSFORMPRIMITIVE3D"_ustr; + case PRIMITIVE3D_ID_HIDDENGEOMETRYPRIMITIVE3D: + return u"HIDDENGEOMETRYPRIMITIVE3D"_ustr; + default: + return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF); + } +} + +} // end of namespace drawinglayer::primitive2d + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/primitive3d/baseprimitive3d.cxx b/drawinglayer/source/primitive3d/baseprimitive3d.cxx index 4a69c7cc0a73..c2c8cc9f7e0f 100644 --- a/drawinglayer/source/primitive3d/baseprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/baseprimitive3d.cxx @@ -29,7 +29,6 @@ using namespace com::sun::star; namespace drawinglayer::primitive3d { BasePrimitive3D::BasePrimitive3D() - : BasePrimitive3DImplBase(m_aMutex) { } @@ -71,14 +70,12 @@ namespace drawinglayer::primitive3d } BufferedDecompositionPrimitive3D::BufferedDecompositionPrimitive3D() - : BasePrimitive3D(), - maBuffered3DDecomposition() { } Primitive3DContainer BufferedDecompositionPrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); if(getBuffered3DDecomposition().empty()) { @@ -98,20 +95,8 @@ namespace drawinglayer::primitive3d if(rCandidate.is()) { - // try to get C++ implementation base - const BasePrimitive3D* pCandidate(dynamic_cast< BasePrimitive3D* >(rCandidate.get())); - - if(pCandidate) - { - // use it if possible - aRetval.expand(pCandidate->getB3DRange(aViewInformation)); - } - else - { - // use UNO API call instead - const uno::Sequence< beans::PropertyValue >& rViewParameters(aViewInformation.getViewInformationSequence()); - aRetval.expand(basegfx::unotools::b3DRectangleFromRealRectangle3D(rCandidate->getRange(rViewParameters))); - } + const BasePrimitive3D* pCandidate(static_cast< BasePrimitive3D* >(rCandidate.get())); + aRetval.expand(pCandidate->getB3DRange(aViewInformation)); } return aRetval; @@ -149,13 +134,8 @@ namespace drawinglayer::primitive3d return true; } - const BasePrimitive3D* pA(dynamic_cast< const BasePrimitive3D* >(rxA.get())); - const BasePrimitive3D* pB(dynamic_cast< const BasePrimitive3D* >(rxB.get())); - - if(!pA || !pB) - { - return false; - } + const BasePrimitive3D* pA(static_cast< const BasePrimitive3D* >(rxA.get())); + const BasePrimitive3D* pB(static_cast< const BasePrimitive3D* >(rxB.get())); return pA->operator==(*pB); } diff --git a/drawinglayer/source/primitive3d/groupprimitive3d.cxx b/drawinglayer/source/primitive3d/groupprimitive3d.cxx index 7a6254b00e72..c50e29f04d8c 100644 --- a/drawinglayer/source/primitive3d/groupprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/groupprimitive3d.cxx @@ -19,20 +19,17 @@ #include <drawinglayer/primitive3d/groupprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d { - GroupPrimitive3D::GroupPrimitive3D( const Primitive3DContainer& rChildren ) - : BasePrimitive3D(), - maChildren(rChildren) + GroupPrimitive3D::GroupPrimitive3D( Primitive3DContainer aChildren ) + : maChildren(std::move(aChildren)) { } - /** The compare opertator uses the Sequence::==operator, so only checking if + /** The compare operator uses the Sequence::==operator, so only checking if the references are equal. All non-equal references are interpreted as non-equal. */ diff --git a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx index 2ea280b68be1..42e2f9e75b71 100644 --- a/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/hatchtextureprimitive3d.cxx @@ -29,6 +29,7 @@ #include <basegfx/matrix/b3dhommatrix.hxx> #include <drawinglayer/primitive3d/polygonprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> +#include <utility> using namespace com::sun::star; @@ -49,215 +50,206 @@ namespace drawinglayer::primitive3d for(size_t a(0); a < nSourceCount; a++) { // get reference - const Primitive3DReference xReference(aSource[a]); + const Primitive3DReference& xReference(aSource[a]); if(xReference.is()) { - // try to cast to BasePrimitive2D implementation - const BasePrimitive3D* pBasePrimitive = dynamic_cast< const BasePrimitive3D* >(xReference.get()); + const BasePrimitive3D* pBasePrimitive = static_cast< const BasePrimitive3D* >(xReference.get()); - if(pBasePrimitive) + // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch + // not all content is needed, remove transparencies and ModifiedColorPrimitives + switch(pBasePrimitive->getPrimitive3DID()) { - // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch - // not all content is needed, remove transparencies and ModifiedColorPrimitives - switch(pBasePrimitive->getPrimitive3DID()) + case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D : { - case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D : + // polyPolygonMaterialPrimitive3D, check texturing and hatching + const PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive); + const basegfx::B3DPolyPolygon& aFillPolyPolygon(rPrimitive.getB3DPolyPolygon()); + + if(maHatch.isFillBackground()) { - // polyPolygonMaterialPrimitive3D, check texturing and hatching - const PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const PolyPolygonMaterialPrimitive3D& >(*pBasePrimitive); - const basegfx::B3DPolyPolygon& aFillPolyPolygon(rPrimitive.getB3DPolyPolygon()); + // add original primitive for background + aDestination.push_back(xReference); + } - if(maHatch.isFillBackground()) + if(aFillPolyPolygon.areTextureCoordinatesUsed()) + { + const sal_uInt32 nPolyCount(aFillPolyPolygon.count()); + basegfx::B2DPolyPolygon aTexPolyPolygon; + basegfx::B2DPoint a2N; + basegfx::B2DVector a2X, a2Y; + basegfx::B3DPoint a3N; + basegfx::B3DVector a3X, a3Y; + bool b2N(false), b2X(false), b2Y(false); + + for(sal_uInt32 b(0); b < nPolyCount; b++) { - // add original primitive for background - aDestination.push_back(xReference); - } + const basegfx::B3DPolygon& aPartPoly(aFillPolyPolygon.getB3DPolygon(b)); + const sal_uInt32 nPointCount(aPartPoly.count()); + basegfx::B2DPolygon aTexPolygon; - if(aFillPolyPolygon.areTextureCoordinatesUsed()) - { - const sal_uInt32 nPolyCount(aFillPolyPolygon.count()); - basegfx::B2DPolyPolygon aTexPolyPolygon; - basegfx::B2DPoint a2N; - basegfx::B2DVector a2X, a2Y; - basegfx::B3DPoint a3N; - basegfx::B3DVector a3X, a3Y; - bool b2N(false), b2X(false), b2Y(false); - - for(sal_uInt32 b(0); b < nPolyCount; b++) + for(sal_uInt32 c(0); c < nPointCount; c++) { - const basegfx::B3DPolygon& aPartPoly(aFillPolyPolygon.getB3DPolygon(b)); - const sal_uInt32 nPointCount(aPartPoly.count()); - basegfx::B2DPolygon aTexPolygon; + const basegfx::B2DPoint a2Candidate(aPartPoly.getTextureCoordinate(c)); - for(sal_uInt32 c(0); c < nPointCount; c++) + if(!b2N) { - const basegfx::B2DPoint a2Candidate(aPartPoly.getTextureCoordinate(c)); - - if(!b2N) - { - a2N = a2Candidate; - a3N = aPartPoly.getB3DPoint(c); - b2N = true; - } - else if(!b2X && !a2N.equal(a2Candidate)) - { - a2X = a2Candidate - a2N; - a3X = aPartPoly.getB3DPoint(c) - a3N; - b2X = true; - } - else if(!b2Y && !a2N.equal(a2Candidate) && !a2X.equal(a2Candidate)) - { - a2Y = a2Candidate - a2N; + a2N = a2Candidate; + a3N = aPartPoly.getB3DPoint(c); + b2N = true; + } + else if(!b2X && !a2N.equal(a2Candidate)) + { + a2X = a2Candidate - a2N; + a3X = aPartPoly.getB3DPoint(c) - a3N; + b2X = true; + } + else if(!b2Y && !a2N.equal(a2Candidate) && !a2X.equal(a2Candidate)) + { + a2Y = a2Candidate - a2N; - const double fCross(a2X.cross(a2Y)); + const double fCross(a2X.cross(a2Y)); - if(!basegfx::fTools::equalZero(fCross)) - { - a3Y = aPartPoly.getB3DPoint(c) - a3N; - b2Y = true; - } + if(!basegfx::fTools::equalZero(fCross)) + { + a3Y = aPartPoly.getB3DPoint(c) - a3N; + b2Y = true; } - - aTexPolygon.append(a2Candidate); } - aTexPolygon.setClosed(true); - aTexPolyPolygon.append(aTexPolygon); + aTexPolygon.append(a2Candidate); } - if(b2N && b2X && b2Y) + aTexPolygon.setClosed(true); + aTexPolyPolygon.append(aTexPolygon); + } + + if(b2N && b2X && b2Y) + { + // found two linearly independent 2D vectors + // get 2d range of texture coordinates + const basegfx::B2DRange aOutlineRange(basegfx::utils::getRange(aTexPolyPolygon)); + const basegfx::BColor aHatchColor(getHatch().getColor()); + const double fAngle(getHatch().getAngle()); + std::vector< basegfx::B2DHomMatrix > aMatrices; + + // get hatch transformations + switch(getHatch().getStyle()) { - // found two linearly independent 2D vectors - // get 2d range of texture coordinates - const basegfx::B2DRange aOutlineRange(basegfx::utils::getRange(aTexPolyPolygon)); - const basegfx::BColor aHatchColor(getHatch().getColor()); - const double fAngle(getHatch().getAngle()); - std::vector< basegfx::B2DHomMatrix > aMatrices; - - // get hatch transformations - switch(getHatch().getStyle()) + case attribute::HatchStyle::Triple: { - case attribute::HatchStyle::Triple: - { - // rotated 45 degrees - texture::GeoTexSvxHatch aHatch( - aOutlineRange, - aOutlineRange, - getHatch().getDistance(), - fAngle - F_PI4); - - aHatch.appendTransformations(aMatrices); - - [[fallthrough]]; - } - case attribute::HatchStyle::Double: - { - // rotated 90 degrees - texture::GeoTexSvxHatch aHatch( - aOutlineRange, - aOutlineRange, - getHatch().getDistance(), - fAngle - F_PI2); + // rotated 45 degrees + texture::GeoTexSvxHatch aHatch( + aOutlineRange, + aOutlineRange, + getHatch().getDistance(), + fAngle - M_PI_4); - aHatch.appendTransformations(aMatrices); + aHatch.appendTransformations(aMatrices); - [[fallthrough]]; - } - case attribute::HatchStyle::Single: - { - // angle as given - texture::GeoTexSvxHatch aHatch( - aOutlineRange, - aOutlineRange, - getHatch().getDistance(), - fAngle); - - aHatch.appendTransformations(aMatrices); - } + [[fallthrough]]; } + case attribute::HatchStyle::Double: + { + // rotated 90 degrees + texture::GeoTexSvxHatch aHatch( + aOutlineRange, + aOutlineRange, + getHatch().getDistance(), + fAngle - M_PI_2); - // create geometry from unit line - basegfx::B2DPolyPolygon a2DHatchLines; - basegfx::B2DPolygon a2DUnitLine; - a2DUnitLine.append(basegfx::B2DPoint(0.0, 0.0)); - a2DUnitLine.append(basegfx::B2DPoint(1.0, 0.0)); + aHatch.appendTransformations(aMatrices); - for(const basegfx::B2DHomMatrix & rMatrix : aMatrices) - { - basegfx::B2DPolygon aNewLine(a2DUnitLine); - aNewLine.transform(rMatrix); - a2DHatchLines.append(aNewLine); + [[fallthrough]]; } - - if(a2DHatchLines.count()) + case attribute::HatchStyle::Single: { - // clip against texture polygon - a2DHatchLines = basegfx::utils::clipPolyPolygonOnPolyPolygon(a2DHatchLines, aTexPolyPolygon, true, true); + // angle as given + texture::GeoTexSvxHatch aHatch( + aOutlineRange, + aOutlineRange, + getHatch().getDistance(), + fAngle); + + aHatch.appendTransformations(aMatrices); } + } + + // create geometry from unit line + basegfx::B2DPolyPolygon a2DHatchLines; + basegfx::B2DPolygon a2DUnitLine; + a2DUnitLine.append(basegfx::B2DPoint(0.0, 0.0)); + a2DUnitLine.append(basegfx::B2DPoint(1.0, 0.0)); + + for(const basegfx::B2DHomMatrix & rMatrix : aMatrices) + { + basegfx::B2DPolygon aNewLine(a2DUnitLine); + aNewLine.transform(rMatrix); + a2DHatchLines.append(aNewLine); + } + + if(a2DHatchLines.count()) + { + // clip against texture polygon + a2DHatchLines = basegfx::utils::clipPolyPolygonOnPolyPolygon(a2DHatchLines, aTexPolyPolygon, true, true); + } - if(a2DHatchLines.count()) + if(a2DHatchLines.count()) + { + // create 2d matrix with 2d vectors as column vectors and 2d point as offset, this represents + // a coordinate system transformation from unit coordinates to the new coordinate system + basegfx::B2DHomMatrix a2D; + a2D.set(0, 0, a2X.getX()); + a2D.set(1, 0, a2X.getY()); + a2D.set(0, 1, a2Y.getX()); + a2D.set(1, 1, a2Y.getY()); + a2D.set(0, 2, a2N.getX()); + a2D.set(1, 2, a2N.getY()); + + // invert that transformation, so we have a back-transformation from texture coordinates + // to unit coordinates + a2D.invert(); + a2DHatchLines.transform(a2D); + + // expand back-transformed geometry to 3D + basegfx::B3DPolyPolygon a3DHatchLines(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(a2DHatchLines, 0.0)); + + // create 3d matrix with 3d vectors as column vectors (0,0,1 as Z) and 3d point as offset, this represents + // a coordinate system transformation from unit coordinates to the object's 3d coordinate system + basegfx::B3DHomMatrix a3D; + a3D.set(0, 0, a3X.getX()); + a3D.set(1, 0, a3X.getY()); + a3D.set(2, 0, a3X.getZ()); + a3D.set(0, 1, a3Y.getX()); + a3D.set(1, 1, a3Y.getY()); + a3D.set(2, 1, a3Y.getZ()); + a3D.set(0, 3, a3N.getX()); + a3D.set(1, 3, a3N.getY()); + a3D.set(2, 3, a3N.getZ()); + + // transform hatch lines to 3D object coordinates + a3DHatchLines.transform(a3D); + + // build primitives from this geometry + const sal_uInt32 nHatchLines(a3DHatchLines.count()); + + for(sal_uInt32 d(0); d < nHatchLines; d++) { - // create 2d matrix with 2d vectors as column vectors and 2d point as offset, this represents - // a coordinate system transformation from unit coordinates to the new coordinate system - basegfx::B2DHomMatrix a2D; - a2D.set(0, 0, a2X.getX()); - a2D.set(1, 0, a2X.getY()); - a2D.set(0, 1, a2Y.getX()); - a2D.set(1, 1, a2Y.getY()); - a2D.set(0, 2, a2N.getX()); - a2D.set(1, 2, a2N.getY()); - - // invert that transformation, so we have a back-transformation from texture coordinates - // to unit coordinates - a2D.invert(); - a2DHatchLines.transform(a2D); - - // expand back-transformed geometry to 3D - basegfx::B3DPolyPolygon a3DHatchLines(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(a2DHatchLines, 0.0)); - - // create 3d matrix with 3d vectors as column vectors (0,0,1 as Z) and 3d point as offset, this represents - // a coordinate system transformation from unit coordinates to the object's 3d coordinate system - basegfx::B3DHomMatrix a3D; - a3D.set(0, 0, a3X.getX()); - a3D.set(1, 0, a3X.getY()); - a3D.set(2, 0, a3X.getZ()); - a3D.set(0, 1, a3Y.getX()); - a3D.set(1, 1, a3Y.getY()); - a3D.set(2, 1, a3Y.getZ()); - a3D.set(0, 3, a3N.getX()); - a3D.set(1, 3, a3N.getY()); - a3D.set(2, 3, a3N.getZ()); - - // transform hatch lines to 3D object coordinates - a3DHatchLines.transform(a3D); - - // build primitives from this geometry - const sal_uInt32 nHatchLines(a3DHatchLines.count()); - - for(sal_uInt32 d(0); d < nHatchLines; d++) - { - const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(a3DHatchLines.getB3DPolygon(d), aHatchColor)); - aDestination.push_back(xRef); - } + const Primitive3DReference xRef(new PolygonHairlinePrimitive3D(a3DHatchLines.getB3DPolygon(d), aHatchColor)); + aDestination.push_back(xRef); } } } - - break; - } - default : - { - // add reference to result - aDestination.push_back(xReference); - break; } + + break; + } + default : + { + // add reference to result + aDestination.push_back(xReference); + break; } - } - else - { - // unknown implementation, add to result - aDestination.push_back(xReference); } } } @@ -276,14 +268,13 @@ namespace drawinglayer::primitive3d } HatchTexturePrimitive3D::HatchTexturePrimitive3D( - const attribute::FillHatchAttribute& rHatch, + attribute::FillHatchAttribute aHatch, const Primitive3DContainer& rChildren, const basegfx::B2DVector& rTextureSize, bool bModulate, bool bFilter) : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter), - maHatch(rHatch), - maBuffered3DDecomposition() + maHatch(std::move(aHatch)) { } @@ -301,12 +292,11 @@ namespace drawinglayer::primitive3d Primitive3DContainer HatchTexturePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& /*rViewInformation*/) const { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); if(getBuffered3DDecomposition().empty()) { - const Primitive3DContainer aNewSequence(impCreate3DDecomposition()); - const_cast< HatchTexturePrimitive3D* >(this)->maBuffered3DDecomposition = aNewSequence; + const_cast<HatchTexturePrimitive3D*>(this)->maBuffered3DDecomposition = impCreate3DDecomposition(); } return getBuffered3DDecomposition(); diff --git a/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx index 1a367e501d29..8d69d8741097 100644 --- a/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/hiddengeometryprimitive3d.cxx @@ -21,9 +21,6 @@ #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> -using namespace com::sun::star; - - namespace drawinglayer::primitive3d { HiddenGeometryPrimitive3D::HiddenGeometryPrimitive3D( diff --git a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx index 255f0235c911..64ae9b949ca1 100644 --- a/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/modifiedcolorprimitive3d.cxx @@ -19,18 +19,16 @@ #include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d { ModifiedColorPrimitive3D::ModifiedColorPrimitive3D( const Primitive3DContainer& rChildren, - const basegfx::BColorModifierSharedPtr& rColorModifier) + basegfx::BColorModifierSharedPtr xColorModifier) : GroupPrimitive3D(rChildren), - maColorModifier(rColorModifier) + maColorModifier(std::move(xColorModifier)) { } diff --git a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx index ac820546b9fa..6127ac77666b 100644 --- a/drawinglayer/source/primitive3d/polygonprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/polygonprimitive3d.cxx @@ -22,6 +22,7 @@ #include <basegfx/polygon/b3dpolypolygon.hxx> #include <primitive3d/polygontubeprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> +#include <utility> using namespace com::sun::star; @@ -30,10 +31,9 @@ using namespace com::sun::star; namespace drawinglayer::primitive3d { PolygonHairlinePrimitive3D::PolygonHairlinePrimitive3D( - const basegfx::B3DPolygon& rPolygon, + basegfx::B3DPolygon aPolygon, const basegfx::BColor& rBColor) - : BasePrimitive3D(), - maPolygon(rPolygon), + : maPolygon(std::move(aPolygon)), maBColor(rBColor) { } @@ -118,13 +118,12 @@ namespace drawinglayer::primitive3d } PolygonStrokePrimitive3D::PolygonStrokePrimitive3D( - const basegfx::B3DPolygon& rPolygon, + basegfx::B3DPolygon aPolygon, const attribute::LineAttribute& rLineAttribute, - const attribute::StrokeAttribute& rStrokeAttribute) - : BufferedDecompositionPrimitive3D(), - maPolygon(rPolygon), + attribute::StrokeAttribute aStrokeAttribute) + : maPolygon(std::move(aPolygon)), maLineAttribute(rLineAttribute), - maStrokeAttribute(rStrokeAttribute) + maStrokeAttribute(std::move(aStrokeAttribute)) { } diff --git a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx index 177d829ef4fc..473d836dc462 100644 --- a/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/polygontubeprimitive3d.cxx @@ -25,8 +25,7 @@ #include <basegfx/polygon/b3dpolypolygontools.hxx> #include <drawinglayer/primitive3d/transformprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> -#include <rtl/instance.hxx> - +#include <mutex> namespace drawinglayer::primitive3d { @@ -39,7 +38,7 @@ namespace drawinglayer::primitive3d Primitive3DContainer m_aLineTubeList; sal_uInt32 m_nLineTubeSegments; attribute::MaterialAttribute3D m_aLineMaterial; - ::osl::Mutex m_aMutex; + std::mutex m_aMutex; public: TubeBuffer() : m_nLineTubeSegments(0) @@ -54,7 +53,7 @@ namespace drawinglayer::primitive3d const attribute::MaterialAttribute3D& rMaterial) { // may exclusively change cached data, use mutex - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); if (nSegments != m_nLineTubeSegments || !(rMaterial == m_aLineMaterial)) { @@ -70,7 +69,7 @@ namespace drawinglayer::primitive3d basegfx::B3DPoint aLastLeft(0.0, 1.0, 0.0); basegfx::B3DPoint aLastRight(1.0, 1.0, 0.0); basegfx::B3DHomMatrix aRot; - aRot.rotate(F_2PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0); + aRot.rotate(2 * M_PI / static_cast<double>(m_nLineTubeSegments), 0.0, 0.0); m_aLineTubeList.resize(m_nLineTubeSegments); for(sal_uInt32 a = 0; a < m_nLineTubeSegments; ++a) @@ -93,9 +92,8 @@ namespace drawinglayer::primitive3d aNewPolygon.setClosed(true); - const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); - const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false)); - m_aLineTubeList[a] = xRef; + basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); + m_aLineTubeList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false); aLastLeft = aNextLeft; aLastRight = aNextRight; @@ -105,16 +103,13 @@ namespace drawinglayer::primitive3d } }; - struct theTubeBuffer : - public rtl::Static< TubeBuffer, theTubeBuffer > {}; - Primitive3DContainer getLineTubeSegments( sal_uInt32 nSegments, const attribute::MaterialAttribute3D& rMaterial) { // static data for buffered tube primitives - TubeBuffer &rTheBuffer = theTubeBuffer::get(); - return rTheBuffer.getLineTubeSegments(nSegments, rMaterial); + static TubeBuffer theTubeBuffer; + return theTubeBuffer.getLineTubeSegments(nSegments, rMaterial); } class CapBuffer @@ -124,7 +119,7 @@ namespace drawinglayer::primitive3d Primitive3DContainer m_aLineCapList; sal_uInt32 m_nLineCapSegments; attribute::MaterialAttribute3D m_aLineMaterial; - ::osl::Mutex m_aMutex; + std::mutex m_aMutex; public: CapBuffer() : m_nLineCapSegments(0) @@ -138,7 +133,7 @@ namespace drawinglayer::primitive3d const attribute::MaterialAttribute3D& rMaterial) { // may exclusively change cached data, use mutex - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); if (nSegments != m_nLineCapSegments || !(rMaterial == m_aLineMaterial)) { @@ -152,7 +147,7 @@ namespace drawinglayer::primitive3d const basegfx::B3DPoint aNull(0.0, 0.0, 0.0); basegfx::B3DPoint aLast(0.0, 1.0, 0.0); basegfx::B3DHomMatrix aRot; - aRot.rotate(F_2PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0); + aRot.rotate(2 * M_PI / static_cast<double>(m_nLineCapSegments), 0.0, 0.0); m_aLineCapList.resize(m_nLineCapSegments); for(sal_uInt32 a = 0; a < m_nLineCapSegments; ++a) @@ -171,9 +166,8 @@ namespace drawinglayer::primitive3d aNewPolygon.setClosed(true); - const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); - const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, m_aLineMaterial, false)); - m_aLineCapList[a] = xRef; + basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); + m_aLineCapList[a] = new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), m_aLineMaterial, false); aLast = aNext; } @@ -183,16 +177,13 @@ namespace drawinglayer::primitive3d } }; - struct theCapBuffer : - public rtl::Static< CapBuffer, theCapBuffer > {}; - Primitive3DContainer getLineCapSegments( sal_uInt32 nSegments, const attribute::MaterialAttribute3D& rMaterial) { // static data for buffered cap primitives - CapBuffer &rTheBuffer = theCapBuffer::get(); - return rTheBuffer.getLineCapSegments(nSegments, rMaterial); + static CapBuffer theCapBuffer; + return theCapBuffer.getLineCapSegments(nSegments, rMaterial); } class CapRoundBuffer @@ -202,7 +193,7 @@ namespace drawinglayer::primitive3d Primitive3DContainer m_aLineCapRoundList; sal_uInt32 m_nLineCapRoundSegments; attribute::MaterialAttribute3D m_aLineMaterial; - ::osl::Mutex m_aMutex; + std::mutex m_aMutex; public: CapRoundBuffer() : m_nLineCapRoundSegments(0) @@ -216,7 +207,7 @@ namespace drawinglayer::primitive3d const attribute::MaterialAttribute3D& rMaterial) { // may exclusively change cached data, use mutex - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); if (nSegments != m_nLineCapRoundSegments || !(rMaterial == m_aLineMaterial)) { @@ -241,8 +232,8 @@ namespace drawinglayer::primitive3d nSegments, nVerSeg, true, - F_PI2, 0.0, - 0.0, F_2PI)); + M_PI_2, 0.0, + 0.0, 2 * M_PI)); const sal_uInt32 nCount(aSphere.count()); if (nCount) @@ -251,7 +242,7 @@ namespace drawinglayer::primitive3d // forget to transform normals, too basegfx::B3DHomMatrix aSphereTrans; - aSphereTrans.rotate(0.0, 0.0, F_PI2); + aSphereTrans.rotate(0.0, 0.0, M_PI_2); aSphere.transform(aSphereTrans); aSphere.transformNormals(aSphereTrans); @@ -261,12 +252,12 @@ namespace drawinglayer::primitive3d for (sal_uInt32 a = 0; a < nCount; ++a) { const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a)); - const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); + basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); // need to create one primitive per Polygon since the primitive // is for planar PolyPolygons which is definitely not the case here m_aLineCapRoundList[a] = new PolyPolygonMaterialPrimitive3D( - aPartPolyPolygon, + std::move(aPartPolyPolygon), rMaterial, false); } @@ -278,17 +269,13 @@ namespace drawinglayer::primitive3d }; - struct theCapRoundBuffer : - public rtl::Static< CapRoundBuffer, theCapRoundBuffer > {}; - - Primitive3DContainer getLineCapRoundSegments( sal_uInt32 nSegments, const attribute::MaterialAttribute3D& rMaterial) { // static data for buffered cap primitives - CapRoundBuffer &rTheBuffer = theCapRoundBuffer::get(); - return rTheBuffer.getLineCapRoundSegments(nSegments, rMaterial); + static CapRoundBuffer theCapRoundBuffer; + return theCapRoundBuffer.getLineCapRoundSegments(nSegments, rMaterial); } Primitive3DContainer getLineJoinSegments( @@ -307,19 +294,18 @@ namespace drawinglayer::primitive3d if(basegfx::B2DLineJoin::Round == aLineJoin) { // calculate new horizontal segments - const sal_uInt32 nHorSeg(basegfx::fround((fAngle / F_2PI) * static_cast<double>(nSegments))); + const sal_uInt32 nHorSeg(basegfx::fround((fAngle / (2 * M_PI)) * static_cast<double>(nSegments))); if(nHorSeg) { // create half-sphere - const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, F_PI2, -F_PI2, 0.0, fAngle)); + const basegfx::B3DPolyPolygon aSphere(basegfx::utils::createUnitSphereFillPolyPolygon(nHorSeg, nVerSeg, true, M_PI_2, -M_PI_2, 0.0, fAngle)); for(sal_uInt32 a(0); a < aSphere.count(); a++) { const basegfx::B3DPolygon& aPartPolygon(aSphere.getB3DPolygon(a)); - const basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); - BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aPartPolyPolygon, rMaterial, false); - aResultVector.push_back(pNew); + basegfx::B3DPolyPolygon aPartPolyPolygon(aPartPolygon); + aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aPartPolyPolygon), rMaterial, false)); } } else @@ -343,13 +329,13 @@ namespace drawinglayer::primitive3d } } - const double fInc(F_PI / static_cast<double>(nVerSeg)); + const double fInc(M_PI / static_cast<double>(nVerSeg)); const double fSin(sin(-fAngle)); const double fCos(cos(-fAngle)); const bool bMiter(basegfx::B2DLineJoin::Miter == aLineJoin); const double fMiterSin(bMiter ? sin(-(fAngle/2.0)) : 0.0); const double fMiterCos(bMiter ? cos(-(fAngle/2.0)) : 0.0); - double fPos(-F_PI2); + double fPos(-M_PI_2); basegfx::B3DPoint aPointOnXY, aPointRotY, aNextPointOnXY, aNextPointRotY; basegfx::B3DPoint aCurrMiter, aNextMiter; basegfx::B3DPolygon aNewPolygon, aMiterPolygon; @@ -465,9 +451,8 @@ namespace drawinglayer::primitive3d // create primitive if(aNewPolygon.count()) { - const basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); - BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aNewPolyPolygon, rMaterial, false); - aResultVector.push_back(pNew); + basegfx::B3DPolyPolygon aNewPolyPolygon(aNewPolygon); + aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aNewPolyPolygon), rMaterial, false)); } if(bMiter && aMiterPolygon.count()) @@ -479,9 +464,8 @@ namespace drawinglayer::primitive3d } // create primitive - const basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon); - BasePrimitive3D* pNew = new PolyPolygonMaterialPrimitive3D(aMiterPolyPolygon, rMaterial, false); - aResultVector.push_back(pNew); + basegfx::B3DPolyPolygon aMiterPolyPolygon(aMiterPolygon); + aResultVector.push_back(new PolyPolygonMaterialPrimitive3D(std::move(aMiterPolyPolygon), rMaterial, false)); } // prepare next step @@ -501,10 +485,7 @@ namespace drawinglayer::primitive3d Primitive3DContainer aRetval(aResultVector.size()); - for(size_t a(0); a < aResultVector.size(); a++) - { - aRetval[a] = Primitive3DReference(aResultVector[a]); - } + std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);}); return aRetval; } @@ -536,7 +517,7 @@ using namespace com::sun::star; if(nPointCount) { - if(basegfx::fTools::more(getRadius(), 0.0)) + if(getRadius() > 0.0) { const attribute::MaterialAttribute3D aMaterial(getBColor()); static const sal_uInt32 nSegments(8); // default for 3d line segments, for more quality just raise this value (in even steps) @@ -553,7 +534,7 @@ using namespace com::sun::star; const basegfx::B3DVector aForw(aNext - aCurr); const double fForwLen(aForw.getLength()); - if(basegfx::fTools::more(fForwLen, 0.0)) + if(fForwLen > 0.0) { // find out if linecap is active const bool bFirst(!a); @@ -615,8 +596,7 @@ using namespace com::sun::star; aSequence = getLineCapSegments(nSegments, aMaterial); } - TransformPrimitive3D* pNewTransformedA = new TransformPrimitive3D(aCapTrans, aSequence); - aResultVector.push_back(pNewTransformedA); + aResultVector.push_back(new TransformPrimitive3D(std::move(aCapTrans), aSequence)); } else { @@ -626,7 +606,7 @@ using namespace com::sun::star; if(!basegfx::fTools::equalZero(fCross)) { // line connect non-parallel, aBack, aForw, use getLineJoin() - const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. F_PI2 + const double fAngle(acos(aBack.scalar(aForw) / (fForwLen * aBack.getLength()))); // 0.0 .. M_PI_2 Primitive3DContainer aNewList( getLineJoinSegments( nSegments, @@ -644,8 +624,8 @@ using namespace com::sun::star; // create trans by rotating unit sphere with angle 90 degrees around Y, then 180-fRot in X. // Also apply usual scaling and translation basegfx::B3DHomMatrix aSphereTrans; - aSphereTrans.rotate(0.0, F_PI2, 0.0); - aSphereTrans.rotate(F_PI - fRotInYZ, 0.0, 0.0); + aSphereTrans.rotate(0.0, M_PI_2, 0.0); + aSphereTrans.rotate(M_PI - fRotInYZ, 0.0, 0.0); aSphereTrans *= aRotVector; aSphereTrans.scale(getRadius(), getRadius(), getRadius()); aSphereTrans.translate(aCurr.getX(), aCurr.getY(), aCurr.getZ()); @@ -653,7 +633,7 @@ using namespace com::sun::star; // line start edge, build transformed primitiveVector3D aResultVector.push_back( new TransformPrimitive3D( - aSphereTrans, + std::move(aSphereTrans), aNewList)); } } @@ -661,7 +641,7 @@ using namespace com::sun::star; // create line segments, build transformed primitiveVector3D aResultVector.push_back( new TransformPrimitive3D( - aTubeTrans, + std::move(aTubeTrans), getLineTubeSegments(nSegments, aMaterial))); if(bNoLineJoin || (!bClosed && bLast)) @@ -670,7 +650,7 @@ using namespace com::sun::star; basegfx::B3DHomMatrix aBackCapTrans; // Mirror (line end) and radius scale - aBackCapTrans.rotate(0.0, F_PI, 0.0); + aBackCapTrans.rotate(0.0, M_PI, 0.0); aBackCapTrans.scale(getRadius(), getRadius(), getRadius()); if(bLineCapSquare && bLast) @@ -704,7 +684,7 @@ using namespace com::sun::star; aResultVector.push_back( new TransformPrimitive3D( - aBackCapTrans, + std::move(aBackCapTrans), aSequence)); } } @@ -717,18 +697,14 @@ using namespace com::sun::star; else { // create hairline - PolygonHairlinePrimitive3D* pNew = new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor()); - aResultVector.push_back(pNew); + aResultVector.push_back(new PolygonHairlinePrimitive3D(getB3DPolygon(), getBColor())); } } // prepare return value Primitive3DContainer aRetval(aResultVector.size()); - for(size_t a(0); a < aResultVector.size(); a++) - { - aRetval[a] = Primitive3DReference(aResultVector[a]); - } + std::transform(aResultVector.cbegin(), aResultVector.cend(), aRetval.begin(), [](auto &rResult){return Primitive3DReference(rResult);}); return aRetval; } @@ -741,7 +717,6 @@ using namespace com::sun::star; double fDegreeStepWidth, double fMiterMinimumAngle) : PolygonHairlinePrimitive3D(rPolygon, rBColor), - maLast3DDecomposition(), mfRadius(fRadius), mfDegreeStepWidth(fDegreeStepWidth), mfMiterMinimumAngle(fMiterMinimumAngle), @@ -768,12 +743,12 @@ using namespace com::sun::star; Primitive3DContainer PolygonTubePrimitive3D::get3DDecomposition(const geometry::ViewInformation3D& rViewInformation) const { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); if(getLast3DDecomposition().empty()) { - const Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation)); - const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = aNewSequence; + Primitive3DContainer aNewSequence(impCreate3DDecomposition(rViewInformation)); + const_cast< PolygonTubePrimitive3D* >(this)->maLast3DDecomposition = std::move(aNewSequence); } return getLast3DDecomposition(); diff --git a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx index db137b47eb28..5a03a89841e0 100644 --- a/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/polypolygonprimitive3d.cxx @@ -20,19 +20,16 @@ #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx> #include <basegfx/polygon/b3dpolypolygontools.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d { PolyPolygonMaterialPrimitive3D::PolyPolygonMaterialPrimitive3D( - const basegfx::B3DPolyPolygon& rPolyPolygon, + basegfx::B3DPolyPolygon aPolyPolygon, const attribute::MaterialAttribute3D& rMaterial, bool bDoubleSided) - : BasePrimitive3D(), - maPolyPolygon(rPolyPolygon), + : maPolyPolygon(std::move(aPolyPolygon)), maMaterial(rMaterial), mbDoubleSided(bDoubleSided) { diff --git a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx index 826583b1404c..d69bb87b865c 100644 --- a/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx +++ b/drawinglayer/source/primitive3d/sdrdecompositiontools3d.cxx @@ -36,6 +36,7 @@ #include <drawinglayer/attribute/sdrfillattribute.hxx> #include <drawinglayer/attribute/sdrshadowattribute.hxx> #include <primitive3d/hiddengeometryprimitive3d.hxx> +#include <rtl/ref.hxx> namespace drawinglayer::primitive3d @@ -136,15 +137,14 @@ namespace drawinglayer::primitive3d // create line and stroke attribute const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap()); - const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen()); + const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen()); // create primitives Primitive3DContainer aRetval(aScaledPolyPolygon.count()); for(sal_uInt32 a(0); a < aScaledPolyPolygon.count(); a++) { - const Primitive3DReference xRef(new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute)); - aRetval[a] = xRef; + aRetval[a] = new PolygonStrokePrimitive3D(aScaledPolyPolygon.getB3DPolygon(a), aLineAttribute, aStrokeAttribute); } if(0.0 != rLine.getTransparence()) @@ -184,7 +184,7 @@ namespace drawinglayer::primitive3d } const Primitive3DReference xRef(new PolyPolygonMaterialPrimitive3D( - aScaledPolyPolygon, + std::move(aScaledPolyPolygon), aSdr3DObjectAttribute.getMaterial(), aSdr3DObjectAttribute.getDoubleSided())); aRetval[a] = xRef; @@ -197,7 +197,7 @@ namespace drawinglayer::primitive3d { bool bModulate(css::drawing::TextureMode_MODULATE == aSdr3DObjectAttribute.getTextureMode()); bool bFilter(aSdr3DObjectAttribute.getTextureFilter()); - BasePrimitive3D* pNewTexturePrimitive3D = nullptr; + rtl::Reference<BasePrimitive3D> pNewTexturePrimitive3D; if(!rFill.getGradient().isDefault()) { @@ -233,18 +233,17 @@ namespace drawinglayer::primitive3d } // exchange aRetval content with texture group - const Primitive3DReference xRef(pNewTexturePrimitive3D); - aRetval = { xRef }; + aRetval = { Primitive3DReference(pNewTexturePrimitive3D) }; if(css::drawing::TextureKind2_LUMINANCE == aSdr3DObjectAttribute.getTextureKind()) { // use modified color primitive to force textures to gray - const basegfx::BColorModifierSharedPtr aBColorModifier = + basegfx::BColorModifierSharedPtr aBColorModifier = std::make_shared<basegfx::BColorModifier_gray>(); - const Primitive3DReference xRef2( + Primitive3DReference xRef2( new ModifiedColorPrimitive3D( aRetval, - aBColorModifier)); + std::move(aBColorModifier))); aRetval = { xRef2 }; } diff --git a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx index afe030ac77b9..2e1071c1f9a6 100644 --- a/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx +++ b/drawinglayer/source/primitive3d/sdrextrudelathetools3d.cxx @@ -71,7 +71,7 @@ namespace { rOuterPolyPolygon = rPolygon; - if(!basegfx::fTools::more(fOffset, 0.0)) + if (fOffset <= 0.0 || basegfx::fTools::equalZero(fOffset)) return; if(bCharacterMode) @@ -129,10 +129,22 @@ namespace if(bCreateTextureCoordinates) { const double fPolygonLengthA(basegfx::utils::getLength(aSubA)); - fTexHorMultiplicatorA = basegfx::fTools::equalZero(fPolygonLengthA) ? 1.0 : 1.0 / fPolygonLengthA; + if (basegfx::fTools::equalZero(fPolygonLengthA)) + fTexHorMultiplicatorA = 1.0; + else + { + assert(fPolygonLengthA != 0 && "help coverity see it's not zero"); + fTexHorMultiplicatorA = 1.0 / fPolygonLengthA; + } const double fPolygonLengthB(basegfx::utils::getLength(aSubB)); - fTexHorMultiplicatorB = basegfx::fTools::equalZero(fPolygonLengthB) ? 1.0 : 1.0 / fPolygonLengthB; + if (basegfx::fTools::equalZero(fPolygonLengthB)) + fTexHorMultiplicatorB = 1.0; + else + { + assert(fPolygonLengthB != 0 && "help coverity see it's not zero"); + fTexHorMultiplicatorB = 1.0 / fPolygonLengthB; + } } for(sal_uInt32 b(0); b < nEdgeCount; b++) @@ -312,26 +324,26 @@ namespace // polygon is closed, one of the points is a member const sal_uInt32 nPointCount(rPoly.count()); - if(nPointCount) - { - basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0)); - const basegfx::B2DVector aVector(rEnd - rStart); + if(!nPointCount) + return false; - for(sal_uInt32 a(0); a < nPointCount; a++) - { - const sal_uInt32 nNextIndex((a + 1) % nPointCount); - const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex)); - const basegfx::B2DVector aEdgeVector(aNext - aCurrent); + basegfx::B2DPoint aCurrent(rPoly.getB2DPoint(0)); + const basegfx::B2DVector aVector(rEnd - rStart); - if(basegfx::utils::findCut( - rStart, aVector, - aCurrent, aEdgeVector) != CutFlagValue::NONE) - { - return true; - } + for(sal_uInt32 a(0); a < nPointCount; a++) + { + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNext(rPoly.getB2DPoint(nNextIndex)); + const basegfx::B2DVector aEdgeVector(aNext - aCurrent); - aCurrent = aNext; + if(basegfx::utils::findCut( + rStart, aVector, + aCurrent, aEdgeVector) != CutFlagValue::NONE) + { + return true; } + + aCurrent = aNext; } return false; @@ -360,7 +372,7 @@ namespace drawinglayer::primitive3d else { const bool bBackScale(!basegfx::fTools::equal(fBackScale, 1.0)); - const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, F_2PI)); + const bool bClosedRotation(!bBackScale && basegfx::fTools::equal(fRotation, 2 * M_PI)); basegfx::B2DPolyPolygon aFront(rSource); basegfx::B2DPolyPolygon aBack(rSource); basegfx::B3DHomMatrix aTransformBack; @@ -619,6 +631,7 @@ namespace drawinglayer::primitive3d if(!basegfx::fTools::equalZero(fTexHeight)) { + assert(fTexHeight != 0 && "help coverity see it's not zero"); fInvTexHeight = 1.0 / fTexHeight; } } diff --git a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx index b7d1c8abdd9b..ed0e8d41c4ab 100644 --- a/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/sdrextrudeprimitive3d.cxx @@ -30,6 +30,7 @@ #include <drawinglayer/attribute/sdrfillattribute.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <utility> using namespace com::sun::star; @@ -234,7 +235,7 @@ namespace drawinglayer::primitive3d // take each angle which deviates more than 10% from going straight as // special edge. This will detect the two outer edges of pie segments, // but not always the center one (think about a near 180 degree pie) - if(F_PI - fabs(fAngle) > F_PI * 0.1) + if(M_PI - fabs(fAngle) > M_PI * 0.1) { if(nPointCount == nIndexA) { @@ -375,7 +376,7 @@ namespace drawinglayer::primitive3d // again when no longer geometry is needed for non-visible 3D objects as it is now for chart if(getPolyPolygon().count() && maSlices.empty()) { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); const_cast< SdrExtrudePrimitive3D& >(*this).impCreateSlices(); } @@ -388,7 +389,7 @@ namespace drawinglayer::primitive3d const basegfx::B2DVector& rTextureSize, const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute, const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute, - const basegfx::B2DPolyPolygon& rPolyPolygon, + basegfx::B2DPolyPolygon aPolyPolygon, double fDepth, double fDiagonal, double fBackScale, @@ -398,9 +399,7 @@ namespace drawinglayer::primitive3d bool bCloseFront, bool bCloseBack) : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute), - maCorrectedPolyPolygon(), - maSlices(), - maPolyPolygon(rPolyPolygon), + maPolyPolygon(std::move(aPolyPolygon)), mfDepth(fDepth), mfDiagonal(fDiagonal), mfBackScale(fBackScale), @@ -411,13 +410,13 @@ namespace drawinglayer::primitive3d mbCloseBack(bCloseBack) { // make sure depth is positive - if(basegfx::fTools::lessOrEqual(getDepth(), 0.0)) + if(getDepth() <= 0.0) { mfDepth = 0.0; } // make sure the percentage value getDiagonal() is between 0.0 and 1.0 - if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0)) + if(getDiagonal() <= 0.0) { mfDiagonal = 0.0; } @@ -482,13 +481,13 @@ namespace drawinglayer::primitive3d (!getBuffered3DDecomposition().empty() && *mpLastRLGViewInformation != rViewInformation)) { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); // conditions of last local decomposition with reduced lines have changed. Remember // new one and clear current decompositiopn SdrExtrudePrimitive3D* pThat = const_cast< SdrExtrudePrimitive3D* >(this); pThat->setBuffered3DDecomposition(Primitive3DContainer()); - pThat->mpLastRLGViewInformation.reset( new geometry::ViewInformation3D(rViewInformation) ); + pThat->mpLastRLGViewInformation = rViewInformation; } } diff --git a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx index 1d5b918a27bf..682ea0c54042 100644 --- a/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/sdrlatheprimitive3d.cxx @@ -26,6 +26,7 @@ #include <drawinglayer/attribute/sdrfillattribute.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <utility> using namespace com::sun::star; @@ -43,7 +44,7 @@ namespace drawinglayer::primitive3d if(!rSliceVector.empty()) { const bool bBackScale(!basegfx::fTools::equal(getBackScale(), 1.0)); - const bool bClosedRotation(!bBackScale && getHorizontalSegments() && basegfx::fTools::equal(getRotation(), F_2PI)); + const bool bClosedRotation(!bBackScale && getHorizontalSegments() && basegfx::fTools::equal(getRotation(), 2 * M_PI)); sal_uInt32 a; // decide what to create @@ -227,7 +228,7 @@ namespace drawinglayer::primitive3d // again when no longer geometry is needed for non-visible 3D objects as it is now for chart if(getPolyPolygon().count() && maSlices.empty()) { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); const_cast< SdrLathePrimitive3D& >(*this).impCreateSlices(); } @@ -240,7 +241,7 @@ namespace drawinglayer::primitive3d const basegfx::B2DVector& rTextureSize, const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute, const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute, - const basegfx::B2DPolyPolygon& rPolyPolygon, + basegfx::B2DPolyPolygon aPolyPolygon, sal_uInt32 nHorizontalSegments, sal_uInt32 nVerticalSegments, double fDiagonal, @@ -252,9 +253,7 @@ namespace drawinglayer::primitive3d bool bCloseFront, bool bCloseBack) : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute), - maCorrectedPolyPolygon(), - maSlices(), - maPolyPolygon(rPolyPolygon), + maPolyPolygon(std::move(aPolyPolygon)), mnHorizontalSegments(nHorizontalSegments), mnVerticalSegments(nVerticalSegments), mfDiagonal(fDiagonal), @@ -267,13 +266,13 @@ namespace drawinglayer::primitive3d mbCloseBack(bCloseBack) { // make sure Rotation is positive - if(basegfx::fTools::lessOrEqual(getRotation(), 0.0)) + if(getRotation() <= 0.0) { mfRotation = 0.0; } // make sure the percentage value getDiagonal() is between 0.0 and 1.0 - if(basegfx::fTools::lessOrEqual(getDiagonal(), 0.0)) + if(getDiagonal() <= 0.0) { mfDiagonal = 0.0; } @@ -340,13 +339,13 @@ namespace drawinglayer::primitive3d (!getBuffered3DDecomposition().empty() && *mpLastRLGViewInformation != rViewInformation)) { - ::osl::MutexGuard aGuard( m_aMutex ); + std::unique_lock aGuard( m_aMutex ); // conditions of last local decomposition with reduced lines have changed. Remember // new one and clear current decompositiopn SdrLathePrimitive3D* pThat = const_cast< SdrLathePrimitive3D* >(this); pThat->setBuffered3DDecomposition(Primitive3DContainer()); - pThat->mpLastRLGViewInformation.reset( new geometry::ViewInformation3D(rViewInformation) ); + pThat->mpLastRLGViewInformation = rViewInformation; } } diff --git a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx index 4887802e17a4..9219cc970c86 100644 --- a/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/sdrpolypolygonprimitive3d.cxx @@ -24,6 +24,7 @@ #include <drawinglayer/attribute/sdrfillattribute.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> #include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <utility> using namespace com::sun::star; @@ -37,8 +38,7 @@ namespace drawinglayer::primitive3d if(getPolyPolygon3D().count()) { - std::vector< basegfx::B3DPolyPolygon > aFill; - aFill.push_back(getPolyPolygon3D()); + std::vector< basegfx::B3DPolyPolygon > aFill { getPolyPolygon3D() }; // get full range const basegfx::B3DRange aRange(getRangeFrom3DGeometry(aFill)); @@ -118,13 +118,13 @@ namespace drawinglayer::primitive3d } SdrPolyPolygonPrimitive3D::SdrPolyPolygonPrimitive3D( - const basegfx::B3DPolyPolygon& rPolyPolygon3D, + basegfx::B3DPolyPolygon aPolyPolygon3D, const basegfx::B3DHomMatrix& rTransform, const basegfx::B2DVector& rTextureSize, const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute, const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute) : SdrPrimitive3D(rTransform, rTextureSize, rSdrLFSAttribute, rSdr3DObjectAttribute), - maPolyPolygon3D(rPolyPolygon3D) + maPolyPolygon3D(std::move(aPolyPolygon3D)) { } diff --git a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx index ad8b9daca186..7a6feb946ebc 100644 --- a/drawinglayer/source/primitive3d/sdrprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/sdrprimitive3d.cxx @@ -20,9 +20,7 @@ #include <drawinglayer/primitive3d/sdrprimitive3d.hxx> #include <basegfx/polygon/b3dpolypolygontools.hxx> #include <drawinglayer/attribute/sdrlineattribute.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d @@ -75,14 +73,13 @@ namespace drawinglayer::primitive3d } SdrPrimitive3D::SdrPrimitive3D( - const basegfx::B3DHomMatrix& rTransform, + basegfx::B3DHomMatrix aTransform, const basegfx::B2DVector& rTextureSize, - const attribute::SdrLineFillShadowAttribute3D& rSdrLFSAttribute, + attribute::SdrLineFillShadowAttribute3D aSdrLFSAttribute, const attribute::Sdr3DObjectAttribute& rSdr3DObjectAttribute) - : BufferedDecompositionPrimitive3D(), - maTransform(rTransform), + : maTransform(std::move(aTransform)), maTextureSize(rTextureSize), - maSdrLFSAttribute(rSdrLFSAttribute), + maSdrLFSAttribute(std::move(aSdrLFSAttribute)), maSdr3DObjectAttribute(rSdr3DObjectAttribute) { } diff --git a/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx index 1e0dd7454124..c3127261f502 100644 --- a/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/sdrsphereprimitive3d.cxx @@ -83,7 +83,7 @@ namespace drawinglayer::primitive3d // different from forced to sphere texture coordinates, // create a old version from it by rotating to old state before applying // the texture coordinates to emulate old behaviour - fRelativeAngle = F_2PI * (static_cast<double>((getHorizontalSegments() >> 1) - 1) / static_cast<double>(getHorizontalSegments())); + fRelativeAngle = 2 * M_PI * (static_cast<double>((getHorizontalSegments() >> 1) - 1) / static_cast<double>(getHorizontalSegments())); basegfx::B3DHomMatrix aRot; aRot.rotate(0.0, fRelativeAngle, 0.0); aFill.transform(aRot); diff --git a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx index cca2e3c6f07f..c32d17dbc69e 100644 --- a/drawinglayer/source/primitive3d/shadowprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/shadowprimitive3d.cxx @@ -19,21 +19,19 @@ #include <primitive3d/shadowprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d { ShadowPrimitive3D::ShadowPrimitive3D( - const basegfx::B2DHomMatrix& rShadowTransform, + basegfx::B2DHomMatrix aShadowTransform, const basegfx::BColor& rShadowColor, double fShadowTransparence, bool bShadow3D, const Primitive3DContainer& rChildren) : GroupPrimitive3D(rChildren), - maShadowTransform(rShadowTransform), + maShadowTransform(std::move(aShadowTransform)), maShadowColor(rShadowColor), mfShadowTransparence(fShadowTransparence), mbShadow3D(bShadow3D) diff --git a/drawinglayer/source/primitive3d/textureprimitive3d.cxx b/drawinglayer/source/primitive3d/textureprimitive3d.cxx index a053a7c214d0..ceeca0489487 100644 --- a/drawinglayer/source/primitive3d/textureprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/textureprimitive3d.cxx @@ -20,6 +20,8 @@ #include <primitive3d/textureprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> #include <basegfx/color/bcolor.hxx> +#include <basegfx/utils/gradienttools.hxx> +#include <utility> using namespace com::sun::star; @@ -91,7 +93,13 @@ namespace drawinglayer::primitive3d { // create TransparenceTexturePrimitive3D with fixed transparence as replacement const basegfx::BColor aGray(getTransparence(), getTransparence(), getTransparence()); - const attribute::FillGradientAttribute aFillGradient(attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, 0.0, aGray, aGray, 1); + + // create ColorStops with StartColor == EndColor == aGray + const basegfx::BColorStops aColorStops { + basegfx::BColorStop(0.0, aGray), + basegfx::BColorStop(1.0, aGray) }; + + const attribute::FillGradientAttribute aFillGradient(css::awt::GradientStyle_LINEAR, 0.0, 0.0, 0.0, 0.0, aColorStops); const Primitive3DReference xRef(new TransparenceTexturePrimitive3D(aFillGradient, getChildren(), getTextureSize())); return { xRef }; } @@ -108,13 +116,13 @@ namespace drawinglayer::primitive3d GradientTexturePrimitive3D::GradientTexturePrimitive3D( - const attribute::FillGradientAttribute& rGradient, + attribute::FillGradientAttribute aGradient, const Primitive3DContainer& rChildren, const basegfx::B2DVector& rTextureSize, bool bModulate, bool bFilter) : TexturePrimitive3D(rChildren, rTextureSize, bModulate, bFilter), - maGradient(rGradient) + maGradient(std::move(aGradient)) { } diff --git a/drawinglayer/source/primitive3d/transformprimitive3d.cxx b/drawinglayer/source/primitive3d/transformprimitive3d.cxx index 1ddb919bf2e1..135cba2c55ab 100644 --- a/drawinglayer/source/primitive3d/transformprimitive3d.cxx +++ b/drawinglayer/source/primitive3d/transformprimitive3d.cxx @@ -19,18 +19,16 @@ #include <drawinglayer/primitive3d/transformprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::primitive3d { TransformPrimitive3D::TransformPrimitive3D( - const basegfx::B3DHomMatrix& rTransformation, + basegfx::B3DHomMatrix aTransformation, const Primitive3DContainer& rChildren) : GroupPrimitive3D(rChildren), - maTransformation(rTransformation) + maTransformation(std::move(aTransformation)) { } diff --git a/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx new file mode 100644 index 000000000000..58f961e2060b --- /dev/null +++ b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx @@ -0,0 +1,302 @@ +/* -*- 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 <drawinglayer/processor2d/SDPRProcessor2dTools.hxx> +#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/graph.hxx> +#include <basegfx/range/b2drange.hxx> + +#ifdef DBG_UTIL +#include <o3tl/environment.hxx> +#include <tools/stream.hxx> +#include <vcl/filter/PngImageWriter.hxx> +#endif + +namespace drawinglayer::processor2d +{ +void setOffsetXYCreatedBitmap( + drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D, + const Bitmap& rBitmap) +{ + rFillGraphicPrimitive2D.impSetOffsetXYCreatedBitmap(rBitmap); +} + +void takeCareOfOffsetXY( + const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D, + Bitmap& rTarget, basegfx::B2DRange& rFillUnitRange) +{ + const attribute::FillGraphicAttribute& rFillGraphicAttribute( + rFillGraphicPrimitive2D.getFillGraphic()); + const bool bOffsetXIsUsed(rFillGraphicAttribute.getOffsetX() > 0.0 + && rFillGraphicAttribute.getOffsetX() < 1.0); + const bool bOffsetYIsUsed(rFillGraphicAttribute.getOffsetY() > 0.0 + && rFillGraphicAttribute.getOffsetY() < 1.0); + + if (bOffsetXIsUsed) + { + if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty()) + { + const Size aSize(rTarget.GetSizePixel()); + const tools::Long w(aSize.Width()); + const tools::Long a( + basegfx::fround<tools::Long>(w * (1.0 - rFillGraphicAttribute.getOffsetX()))); + + if (0 != a && w != a) + { + const tools::Long h(aSize.Height()); + const tools::Long b(w - a); + Bitmap aTarget(Size(w, h * 2), rTarget.getPixelFormat()); + + aTarget.SetPrefSize( + Size(rTarget.GetPrefSize().Width(), rTarget.GetPrefSize().Height() * 2)); + const tools::Rectangle aSrcDst(Point(), aSize); + aTarget.CopyPixel(aSrcDst, // Dst + aSrcDst, // Src + rTarget); + const Size aSizeA(b, h); + aTarget.CopyPixel(tools::Rectangle(Point(0, h), aSizeA), // Dst + tools::Rectangle(Point(a, 0), aSizeA), // Src + rTarget); + const Size aSizeB(a, h); + aTarget.CopyPixel(tools::Rectangle(Point(b, h), aSizeB), // Dst + tools::Rectangle(Point(), aSizeB), // Src + rTarget); + + setOffsetXYCreatedBitmap( + const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>( + rFillGraphicPrimitive2D), + aTarget); + } + } + + if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty()) + { + rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap(); + rFillUnitRange.expand(basegfx::B2DPoint( + rFillUnitRange.getMinX(), rFillUnitRange.getMaxY() + rFillUnitRange.getHeight())); + } + } + else if (bOffsetYIsUsed) + { + if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty()) + { + const Size aSize(rTarget.GetSizePixel()); + const tools::Long h(aSize.Height()); + const tools::Long a( + basegfx::fround<tools::Long>(h * (1.0 - rFillGraphicAttribute.getOffsetY()))); + + if (0 != a && h != a) + { + const tools::Long w(aSize.Width()); + const tools::Long b(h - a); + Bitmap aTarget(Size(w * 2, h), rTarget.getPixelFormat()); + + aTarget.SetPrefSize( + Size(rTarget.GetPrefSize().Width() * 2, rTarget.GetPrefSize().Height())); + const tools::Rectangle aSrcDst(Point(), aSize); + aTarget.CopyPixel(aSrcDst, // Dst + aSrcDst, // Src + rTarget); + const Size aSizeA(w, b); + aTarget.CopyPixel(tools::Rectangle(Point(w, 0), aSizeA), // Dst + tools::Rectangle(Point(0, a), aSizeA), // Src + rTarget); + const Size aSizeB(w, a); + aTarget.CopyPixel(tools::Rectangle(Point(w, b), aSizeB), // Dst + tools::Rectangle(Point(), aSizeB), // Src + rTarget); + + setOffsetXYCreatedBitmap( + const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>( + rFillGraphicPrimitive2D), + aTarget); + } + } + + if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty()) + { + rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap(); + rFillUnitRange.expand(basegfx::B2DPoint( + rFillUnitRange.getMaxX() + rFillUnitRange.getWidth(), rFillUnitRange.getMinY())); + } + } +} + +bool prepareBitmapForDirectRender( + const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, Bitmap& rTarget, + basegfx::B2DRange& rFillUnitRange, double fBigDiscreteArea) +{ + const attribute::FillGraphicAttribute& rFillGraphicAttribute( + rFillGraphicPrimitive2D.getFillGraphic()); + const Graphic& rGraphic(rFillGraphicAttribute.getGraphic()); + + if (rFillGraphicAttribute.isDefault() || rGraphic.IsNone()) + { + // default attributes or GraphicType::NONE, so no fill .-> done + return false; + } + + if (!rFillGraphicAttribute.getTiling()) + { + // If no tiling used, the Graphic will need to be painted just once. This + // is perfectly done using the decomposition, so use it. + // What we want to do here is to optimize tiled paint, for two reasons: + // (a) speed: draw one tile, repeat -> obvious + // (b) correctness: not so obvious, but since in AAed paint the same edge + // of touching polygons both AAed do *not* sum up, but get blended by + // multiplication (0.5 * 0.5 -> 0.25) the connection will stay visible, + // not only with filled polygons, but also with bitmaps + // Signal that paint is needed + return true; + } + + if (rFillUnitRange.isEmpty()) + { + // no fill range definition, no fill, done + return false; + } + + const basegfx::B2DHomMatrix aLocalTransform(rViewInformation2D.getObjectToViewTransformation() + * rFillGraphicPrimitive2D.getTransformation()); + const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport()); + + if (!rDiscreteViewPort.isEmpty()) + { + // calculate discrete covered pixel area + basegfx::B2DRange aDiscreteRange(basegfx::B2DRange::getUnitB2DRange()); + aDiscreteRange.transform(aLocalTransform); + + if (!aDiscreteRange.overlaps(rDiscreteViewPort)) + { + // we have a Viewport and visible range of geometry is outside -> not visible, done + return false; + } + } + + if (GraphicType::Bitmap == rGraphic.GetType() && rGraphic.IsAnimated()) + { + // Need to prepare specialized AnimatedGraphicPrimitive2D, + // cannot handle here. Signal that paint is needed + return true; + } + + if (GraphicType::Bitmap == rGraphic.GetType() && !rGraphic.getVectorGraphicData()) + { + // bitmap graphic, always handle locally, so get bitmap data independent + // if it'sie or it's discrete display size + rTarget = rGraphic.GetBitmap(); + } + else + { + // Vector Graphic Data fill, including metafile: + // We can know about discrete pixel size here, calculate and use it. + // To do so, using Vectors is sufficient to get the lengths. It is + // not necessary to transform the whole target coordinate system. + const basegfx::B2DVector aDiscreteXAxis( + aLocalTransform + * basegfx::B2DVector(rFillUnitRange.getMaxX() - rFillUnitRange.getMinX(), 0.0)); + const basegfx::B2DVector aDiscreteYAxis( + aLocalTransform + * basegfx::B2DVector(0.0, rFillUnitRange.getMaxY() - rFillUnitRange.getMinY())); + + // get and ensure minimal size + const double fDiscreteWidth(std::max(1.0, aDiscreteXAxis.getLength())); + const double fDiscreteHeight(std::max(1.0, aDiscreteYAxis.getLength())); + + // compare with a big visualization size in discrete pixels + const double fTargetDiscreteArea(fDiscreteWidth * fDiscreteHeight); + + if (fTargetDiscreteArea > fBigDiscreteArea) + { + // When the vector data is visualized big it is better to not handle here + // but use decomposition fallback which then will visualize the vector data + // directly -> better quality, acceptable number of tile repeat(s) + // signal that paint is needed + return true; + } + else + { + // If visualized small, the amount of repeated fills gets expensive, so + // in that case use a Bitmap and the Brush technique below. + // The Bitmap may be created here exactly for the needed target size + // (using local D2DBitmapPixelProcessor2D and the vector data), + // but since we have a HW renderer and re-use of system-dependent data + // at Bitmap is possible, just get the default fallback Bitmap from the + // vector data to continue. Trust the existing converters for now to + // do something with good quality. + rTarget = rGraphic.GetBitmap(); + } + } + + if (rTarget.IsEmpty() || rTarget.GetSizePixel().IsEmpty()) + { + // no pixel data, done + return false; + } + + // react if OffsetX/OffsetY of the FillGraphicAttribute is used + takeCareOfOffsetXY(rFillGraphicPrimitive2D, rTarget, rFillUnitRange); + +#ifdef DBG_UTIL + // allow to check bitmap data, e.g. control OffsetX/OffsetY stuff + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + if (bDoSaveForVisualControl) + { + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) + { + SvFileStream aNew(sDumpPath + "test_getreplacement.png", + StreamMode::WRITE | StreamMode::TRUNC); + vcl::PngImageWriter aPNGWriter(aNew); + aPNGWriter.write(rTarget); + } + } +#endif + + // signal to render it + return true; +} + +void calculateDiscreteVisibleRange( + basegfx::B2DRange& rDiscreteVisibleRange, const basegfx::B2DRange& rContentRange, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) +{ + if (rContentRange.isEmpty()) + { + // no content, done + rDiscreteVisibleRange.reset(); + return; + } + + basegfx::B2DRange aDiscreteRange(rContentRange); + aDiscreteRange.transform(rViewInformation2D.getObjectToViewTransformation()); + const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport()); + rDiscreteVisibleRange = aDiscreteRange; + + if (!rDiscreteViewPort.isEmpty()) + { + rDiscreteVisibleRange.intersect(rDiscreteViewPort); + } +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/baseprocessor2d.cxx b/drawinglayer/source/processor2d/baseprocessor2d.cxx index 9d1671dcf959..5d71ab4fd7f7 100644 --- a/drawinglayer/source/processor2d/baseprocessor2d.cxx +++ b/drawinglayer/source/processor2d/baseprocessor2d.cxx @@ -17,11 +17,9 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> #include <drawinglayer/processor2d/baseprocessor2d.hxx> -#include <comphelper/sequence.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::processor2d @@ -30,8 +28,8 @@ namespace drawinglayer::processor2d { } - BaseProcessor2D::BaseProcessor2D(const geometry::ViewInformation2D& rViewInformation) - : maViewInformation2D(rViewInformation) + BaseProcessor2D::BaseProcessor2D(geometry::ViewInformation2D aViewInformation) + : maViewInformation2D(std::move(aViewInformation)) { } @@ -41,43 +39,46 @@ namespace drawinglayer::processor2d void BaseProcessor2D::process(const primitive2d::BasePrimitive2D& rCandidate) { - primitive2d::Primitive2DContainer aContainer; - rCandidate.get2DDecomposition(aContainer, getViewInformation2D()); - process(aContainer); + // use the visitor API to avoid the cost of constructing Primitive2DContainers + rCandidate.get2DDecomposition(*this, getViewInformation2D()); } - void BaseProcessor2D::process(const primitive2d::Primitive2DContainer& rSource) + // Primitive2DDecompositionVisitor + void BaseProcessor2D::visit(const primitive2d::Primitive2DReference& rCandidate) { - if(rSource.empty()) - return; - - const sal_Int32 nCount(rSource.size()); + if (rCandidate) + processBasePrimitive2D(*rCandidate); + } + void BaseProcessor2D::visit(const primitive2d::Primitive2DContainer& rContainer) + { + process(rContainer); + } + void BaseProcessor2D::visit(primitive2d::Primitive2DContainer&& rCandidate) + { + process(rCandidate); + } - for(sal_Int32 a(0); a < nCount; a++) + void BaseProcessor2D::process(const primitive2d::Primitive2DContainer& rSource) + { + for (const primitive2d::Primitive2DReference& rCandidate : rSource) { - // get reference - const primitive2d::Primitive2DReference xReference(rSource[a]); + // Skip, if not visible + if (rCandidate && rCandidate->getVisible()) + processBasePrimitive2D(*rCandidate); + } + } - if(xReference.is()) - { - // try to cast to BasePrimitive2D implementation - const primitive2d::BasePrimitive2D* pBasePrimitive = dynamic_cast< const primitive2d::BasePrimitive2D* >(xReference.get()); + void BaseProcessor2D::setViewInformation2D(const geometry::ViewInformation2D& rNew) + { + if (rNew != maViewInformation2D) + { + // set if changed + maViewInformation2D = rNew; - if(pBasePrimitive) - { - // it is a BasePrimitive2D implementation, use local processor - processBasePrimitive2D(*pBasePrimitive); - } - else - { - // unknown implementation, use UNO API call instead and process recursively - const uno::Sequence< beans::PropertyValue >& rViewParameters(getViewInformation2D().getViewInformationSequence()); - process(comphelper::sequenceToContainer<primitive2d::Primitive2DContainer>(xReference->getDecomposition(rViewParameters))); - } - } + // allow reaction on change + onViewInformation2DChanged(); } } - } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx new file mode 100644 index 000000000000..c54a223c1e5c --- /dev/null +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -0,0 +1,4584 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <drawinglayer/processor2d/cairopixelprocessor2d.hxx> +#include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx> +#include <sal/log.hxx> +#include <vcl/BitmapTools.hxx> +#include <vcl/BitmapWriteAccess.hxx> +#include <vcl/alpha.hxx> +#include <vcl/cairo.hxx> +#include <vcl/CairoFormats.hxx> +#include <vcl/outdev.hxx> +#include <vcl/sysdata.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/lok.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/Tools.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/invertprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> +#include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <drawinglayer/converters.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/utils/systemdependentdata.hxx> +#include <basegfx/utils/bgradient.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/vcllayout.hxx> +#include <officecfg/Office/Common.hxx> +#include <com/sun/star/awt/XView.hpp> +#include <com/sun/star/awt/XControl.hpp> +#include <unordered_map> +#include <dlfcn.h> + +using namespace com::sun::star; + +namespace +{ +void impl_cairo_set_hairline(cairo_t* pRT, + const drawinglayer::geometry::ViewInformation2D& rViewInformation, + bool bCairoCoordinateLimitWorkaroundActive) +{ +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + void* addr(dlsym(nullptr, "cairo_set_hairline")); + if (nullptr != addr) + { + cairo_set_hairline(pRT, true); + return; + } +#endif + if (bCairoCoordinateLimitWorkaroundActive) + { + // we have to render in view coordinates, set line width to 1.0 + cairo_set_line_width(pRT, 1.0); + } + else + { + // avoid cairo_device_to_user_distance, see note on that below + const double fPx( + (rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)) + .getLength()); + cairo_set_line_width(pRT, fPx); + } +} + +void addB2DPolygonToPathGeometry(cairo_t* pRT, const basegfx::B2DPolygon& rPolygon) +{ + const sal_uInt32 nPointCount(rPolygon.count()); + + if (0 == nPointCount) + // no points, done + return; + + // get basic infos + const bool bClosed(rPolygon.isClosed()); + const sal_uInt32 nEdgeCount(bClosed ? nPointCount : nPointCount - 1); + + // get 1st point and move to it + basegfx::B2DPoint aCurrent(rPolygon.getB2DPoint(0)); + cairo_move_to(pRT, aCurrent.getX(), aCurrent.getY()); + + for (sal_uInt32 nIndex(0); nIndex < nEdgeCount; nIndex++) + { + // get index for and next point + const sal_uInt32 nNextIndex((nIndex + 1) % nPointCount); + const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex)); + + // get and check curve stuff + basegfx::B2DPoint aCP1(rPolygon.getNextControlPoint(nIndex)); + basegfx::B2DPoint aCP2(rPolygon.getPrevControlPoint(nNextIndex)); + const bool bCP1Equal(aCP1.equal(aCurrent)); + const bool bCP2Equal(aCP2.equal(aNext)); + + if (!bCP1Equal || !bCP2Equal) + { + // tdf#99165, see other similar changes for more info + if (bCP1Equal) + aCP1 = aCurrent + ((aCP2 - aCurrent) * 0.0005); + + if (bCP2Equal) + aCP2 = aNext + ((aCP1 - aNext) * 0.0005); + + cairo_curve_to(pRT, aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(), aNext.getX(), + aNext.getY()); + } + else + { + cairo_line_to(pRT, aNext.getX(), aNext.getY()); + } + + // prepare next step + aCurrent = aNext; + } + + if (bClosed) + cairo_close_path(pRT); +} + +// needed as helper, see below. It guarantees clean +// construction/cleanup using destructor +// NOTE: maybe mpSurface can be constructed even simpler, +// not sure about that. It is only used to construct +// and hold path data +struct CairoContextHolder +{ + cairo_surface_t* mpSurface; + cairo_t* mpRenderContext; + + CairoContextHolder() + : mpSurface(cairo_image_surface_create(CAIRO_FORMAT_A1, 1, 1)) + , mpRenderContext(cairo_create(mpSurface)) + { + } + + ~CairoContextHolder() + { + cairo_destroy(mpRenderContext); + cairo_surface_destroy(mpSurface); + } + + cairo_t* getContext() const { return mpRenderContext; } +}; + +// global static helper instance +CairoContextHolder globalStaticCairoContext; + +// it shows that re-using and buffering path geometry data using +// cairo is more complicated than initially thought: when adding +// a path to a cairo_t render context it already *uses* the set +// transformation, also usually consumes the path when painting. +// The (only available) method cairo_copy_path to preserve that +// data *also* transforms the path - if not already created in +// transformed form - using the current transformation set at the +// cairo context. +// This is not what we want to have a re-usable path that is +// buffered at the Poly(poly)gon: we explicitly want *exactly* +// the coordinates in the polygon preserved *at* the polygon to +// be able to re-use that data independent from any set +// transformation at any cairo context. +// Thus, create paths using a helper (CairoPathHelper) using a +// helper cairo context (CairoContextHolder) that never gets +// transformed. This removes the need to feed it the cairo context, +// but also does not immediately add the path data to the target +// context, that needs to be done using cairo_append_path at the +// target cairo context. That works since all geometry is designed +// to use exactly that coordinate system the polygon is already +// designed for anyways, and it transforms as needed inside the +// target cairo context as needed (if transform is set) +class CairoPathHelper +{ + // the created CairoPath + cairo_path_t* mpCairoPath; + +public: + CairoPathHelper(const basegfx::B2DPolygon& rPolygon) + : mpCairoPath(nullptr) + { + cairo_new_path(globalStaticCairoContext.getContext()); + addB2DPolygonToPathGeometry(globalStaticCairoContext.getContext(), rPolygon); + mpCairoPath = cairo_copy_path(globalStaticCairoContext.getContext()); + } + + CairoPathHelper(const basegfx::B2DPolyPolygon& rPolyPolygon) + : mpCairoPath(nullptr) + { + cairo_new_path(globalStaticCairoContext.getContext()); + for (const auto& rPolygon : rPolyPolygon) + addB2DPolygonToPathGeometry(globalStaticCairoContext.getContext(), rPolygon); + mpCairoPath = cairo_copy_path(globalStaticCairoContext.getContext()); + } + + ~CairoPathHelper() + { + // need to cleanup instance + cairo_path_destroy(mpCairoPath); + } + + // read access + cairo_path_t* getCairoPath() const { return mpCairoPath; } + + sal_Int64 getEstimatedSize() const + { + if (nullptr == mpCairoPath) + return 0; + + // per node: + // - num_data incarnations of + // - sizeof(cairo_path_data_t) which is a union of defines and point data + // thus may 2 x sizeof(double) + return mpCairoPath->num_data * sizeof(cairo_path_data_t); + } +}; + +class SystemDependentData_CairoPathGeometry : public basegfx::SystemDependentData +{ + // the CairoPath holder + std::shared_ptr<CairoPathHelper> mpCairoPathHelper; + +public: + SystemDependentData_CairoPathGeometry(const std::shared_ptr<CairoPathHelper>& pCairoPathHelper) + : basegfx::SystemDependentData(Application::GetSystemDependentDataManager(), + basegfx::SDD_Type::SDDType_CairoPathGeometry) + , mpCairoPathHelper(pCairoPathHelper) + { + } + + // read access + const std::shared_ptr<CairoPathHelper>& getCairoPathHelper() const { return mpCairoPathHelper; } + + virtual sal_Int64 estimateUsageInBytes() const override + { + return (nullptr != mpCairoPathHelper) ? mpCairoPathHelper->getEstimatedSize() : 0; + } +}; + +constexpr unsigned long nMinimalPointsPath(4); +constexpr unsigned long nMinimalPointsFill(12); + +void checkAndDoPixelSnap(cairo_t* pRT, + const drawinglayer::geometry::ViewInformation2D& rViewInformation) +{ + const bool bPixelSnap(rViewInformation.getPixelSnapHairline() + && rViewInformation.getUseAntiAliasing()); + + if (!bPixelSnap) + // no pixel snap, done + return; + + // with the comments above at CairoPathHelper we cannot do PixelSnap + // at path construction time, so it needs to be done *after* the path + // data is added to the cairo context. Advantage is that all general + // path data can be buffered, though, but needs view-dependent manipulation + // here after being added. + // For now, just snap all points - no real need to identify hor/ver lines + // when you think about it + + // get helper path + cairo_path_t* path(cairo_copy_path(pRT)); + + if (0 == path->num_data) + { + // path is empty, done + cairo_path_destroy(path); + return; + } + + auto doPixelSnap([&pRT](double& rX, double& rY) { + // transform to discrete pixels + cairo_user_to_device(pRT, &rX, &rY); + + // round them, also add 0.5 which will be as transform in + // the paint method to move to 'inside' pixels when AA used. + // remember: this is only done when AA is active (see bPixelSnap + // above) and moves the hairline to full-pixel position + rX = trunc(rX) + 0.5; + rY = trunc(rY) + 0.5; + + // transform back to former transformed state + cairo_device_to_user(pRT, &rX, &rY); + }); + + for (int a(0); a < path->num_data; a += path->data[a].header.length) + { + cairo_path_data_t* data(&path->data[a]); + + switch (data->header.type) + { + case CAIRO_PATH_CURVE_TO: + { + // curve: snap all three point positions, + // thus use fallthrough below + doPixelSnap(data[2].point.x, data[2].point.y); + doPixelSnap(data[3].point.x, data[3].point.y); + [[fallthrough]]; // break; + } + case CAIRO_PATH_MOVE_TO: + case CAIRO_PATH_LINE_TO: + { + // path/move: snap first point position + doPixelSnap(data[1].point.x, data[1].point.y); + break; + } + case CAIRO_PATH_CLOSE_PATH: + { + break; + } + } + } + + // set changed path back at cairo context + cairo_new_path(pRT); + cairo_append_path(pRT, path); + + // destroy helper path + cairo_path_destroy(path); +} + +void getOrCreatePathGeometry(cairo_t* pRT, const basegfx::B2DPolygon& rPolygon, + const drawinglayer::geometry::ViewInformation2D& rViewInformation, + bool bPixelSnap) +{ + // try to access buffered data + std::shared_ptr<SystemDependentData_CairoPathGeometry> pSystemDependentData_CairoPathGeometry( + rPolygon.getSystemDependentData<SystemDependentData_CairoPathGeometry>( + basegfx::SDD_Type::SDDType_CairoPathGeometry)); + + if (pSystemDependentData_CairoPathGeometry) + { + // re-use data and do evtl. needed pixel snap after adding on cairo path data + cairo_append_path( + pRT, pSystemDependentData_CairoPathGeometry->getCairoPathHelper()->getCairoPath()); + if (bPixelSnap) + checkAndDoPixelSnap(pRT, rViewInformation); + return; + } + + // create new data and add path data to pRT and do evtl. needed pixel snap after adding on cairo path data + std::shared_ptr<CairoPathHelper> pCairoPathHelper(std::make_shared<CairoPathHelper>(rPolygon)); + cairo_append_path(pRT, pCairoPathHelper->getCairoPath()); + if (bPixelSnap) + checkAndDoPixelSnap(pRT, rViewInformation); + + // add to buffering mechanism if not trivial + if (rPolygon.count() > nMinimalPointsPath) + rPolygon.addOrReplaceSystemDependentData<SystemDependentData_CairoPathGeometry>( + pCairoPathHelper); +} + +void getOrCreateFillGeometry(cairo_t* pRT, const basegfx::B2DPolyPolygon& rPolyPolygon) +{ + // try to access buffered data + std::shared_ptr<SystemDependentData_CairoPathGeometry> pSystemDependentData_CairoPathGeometry( + rPolyPolygon.getSystemDependentData<SystemDependentData_CairoPathGeometry>( + basegfx::SDD_Type::SDDType_CairoPathGeometry)); + + if (pSystemDependentData_CairoPathGeometry) + { + // re-use data + cairo_append_path( + pRT, pSystemDependentData_CairoPathGeometry->getCairoPathHelper()->getCairoPath()); + return; + } + + // create new data and add path data to pRT + std::shared_ptr<CairoPathHelper> pCairoPathHelper( + std::make_shared<CairoPathHelper>(rPolyPolygon)); + cairo_append_path(pRT, pCairoPathHelper->getCairoPath()); + + // get all PointCount to detect non-trivial + sal_uInt32 nAllPointCount(0); + for (const auto& rPolygon : rPolyPolygon) + nAllPointCount += rPolygon.count(); + + // add to buffering mechanism when no PixelSnapHairline (see above) and not trivial + if (nAllPointCount > nMinimalPointsFill) + rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_CairoPathGeometry>( + pCairoPathHelper); +} + +// check for env var that decides for using downscale pattern +const char* pDisableDownScale(getenv("SAL_DISABLE_CAIRO_DOWNSCALE")); +const bool bDisableDownScale(nullptr != pDisableDownScale); +constexpr unsigned long nMinimalDiscreteSize(15); +constexpr unsigned long nHalfMDSize((nMinimalDiscreteSize + 1) / 2); +constexpr unsigned long +nMinimalDiscreteSquareSizeToBuffer(nMinimalDiscreteSize* nMinimalDiscreteSize); + +class CairoSurfaceHelper +{ + // the buffered CairoSurface (bitmap data) + cairo_surface_t* mpCairoSurface; + + // evtl. MipMapped data (pre-scale to reduce data processing load) + mutable std::unordered_map<sal_uInt64, cairo_surface_t*> maDownscaled; + + // create 32bit RGBA data for given Bitmap + void createRGBA(const Bitmap& rBitmap) + { + BitmapScopedReadAccess pReadAccess(rBitmap); + const tools::Long nHeight(pReadAccess->Height()); + const tools::Long nWidth(pReadAccess->Width()); + mpCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, nWidth, nHeight); + if (cairo_surface_status(mpCairoSurface) != CAIRO_STATUS_SUCCESS) + { + SAL_WARN("drawinglayer", + "cairo_image_surface_create failed for: " << nWidth << " x " << nHeight); + return; + } + const sal_uInt32 nStride(cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, nWidth)); + unsigned char* surfaceData(cairo_image_surface_get_data(mpCairoSurface)); + + for (tools::Long y(0); y < nHeight; ++y) + { + unsigned char* pPixelData(surfaceData + (nStride * y)); + + for (tools::Long x(0); x < nWidth; ++x) + { + const BitmapColor aColor(pReadAccess->GetColor(y, x)); + const sal_uInt16 nAlpha(aColor.GetAlpha()); + + pPixelData[SVP_CAIRO_RED] = vcl::bitmap::premultiply(aColor.GetRed(), nAlpha); + pPixelData[SVP_CAIRO_GREEN] = vcl::bitmap::premultiply(aColor.GetGreen(), nAlpha); + pPixelData[SVP_CAIRO_BLUE] = vcl::bitmap::premultiply(aColor.GetBlue(), nAlpha); + pPixelData[SVP_CAIRO_ALPHA] = nAlpha; + pPixelData += 4; + } + } + + cairo_surface_mark_dirty(mpCairoSurface); + } + + // create 32bit RGB data for given Bitmap + void createRGB(const Bitmap& rBitmap) + { + BitmapScopedReadAccess pReadAccess(rBitmap); + const tools::Long nHeight(pReadAccess->Height()); + const tools::Long nWidth(pReadAccess->Width()); + mpCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, nWidth, nHeight); + if (cairo_surface_status(mpCairoSurface) != CAIRO_STATUS_SUCCESS) + { + SAL_WARN("drawinglayer", + "cairo_image_surface_create failed for: " << nWidth << " x " << nHeight); + return; + } + sal_uInt32 nStride(cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, nWidth)); + unsigned char* surfaceData(cairo_image_surface_get_data(mpCairoSurface)); + + for (tools::Long y(0); y < nHeight; ++y) + { + unsigned char* pPixelData(surfaceData + (nStride * y)); + + for (tools::Long x(0); x < nWidth; ++x) + { + const BitmapColor aColor(pReadAccess->GetColor(y, x)); + + pPixelData[SVP_CAIRO_RED] = aColor.GetRed(); + pPixelData[SVP_CAIRO_GREEN] = aColor.GetGreen(); + pPixelData[SVP_CAIRO_BLUE] = aColor.GetBlue(); + pPixelData[SVP_CAIRO_ALPHA] = 255; // not really needed + pPixelData += 4; + } + } + + cairo_surface_mark_dirty(mpCairoSurface); + } + +// #define TEST_RGB16 +#ifdef TEST_RGB16 + // experimental: create 16bit RGB data for given Bitmap + void createRGB16(const Bitmap& rBitmap) + { + BitmapScopedReadAccess pReadAccess(rBitmap); + const tools::Long nHeight(pReadAccess->Height()); + const tools::Long nWidth(pReadAccess->Width()); + mpCairoSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB16_565, nWidth, nHeight); + if (cairo_surface_status(mpCairoSurface) != CAIRO_STATUS_SUCCESS) + { + SAL_WARN("drawinglayer", + "cairo_image_surface_create failed for: " << nWidth << " x " << nHeight); + return; + } + sal_uInt32 nStride(cairo_format_stride_for_width(CAIRO_FORMAT_RGB16_565, nWidth)); + unsigned char* surfaceData(cairo_image_surface_get_data(mpCairoSurface)); + + for (tools::Long y(0); y < nHeight; ++y) + { + unsigned char* pPixelData(surfaceData + (nStride * y)); + + for (tools::Long x(0); x < nWidth; ++x) + { + const BitmapColor aColor(pReadAccess->GetColor(y, x)); + const sal_uInt8 aLeft((aColor.GetBlue() >> 3) | ((aColor.GetGreen() << 3) & 0xe0)); + const sal_uInt8 aRight((aColor.GetRed() & 0xf8) | (aColor.GetGreen() >> 5)); +#ifdef OSL_BIGENDIAN + pPixelData[1] = aRight; + pPixelData[0] = aLeft; +#else + pPixelData[0] = aLeft; + pPixelData[1] = aRight; +#endif + pPixelData += 2; + } + } + + cairo_surface_mark_dirty(mpCairoSurface); + } +#endif + +public: + CairoSurfaceHelper(const Bitmap& rBitmap) + : mpCairoSurface(nullptr) + , maDownscaled() + { + if (rBitmap.HasAlpha()) + createRGBA(rBitmap); + else +#ifdef TEST_RGB16 + createRGB16(rBitmap); +#else + createRGB(rBitmap); +#endif + } + + ~CairoSurfaceHelper() + { + // cleanup surface + cairo_surface_destroy(mpCairoSurface); + + // cleanup MipMap surfaces + for (auto& candidate : maDownscaled) + cairo_surface_destroy(candidate.second); + } + + cairo_surface_t* getCairoSurface(sal_uInt32 nTargetWidth = 0, + sal_uInt32 nTargetHeight = 0) const + { + // in simple cases just return the single created surface + if (bDisableDownScale || nullptr == mpCairoSurface || 0 == nTargetWidth + || 0 == nTargetHeight) + return mpCairoSurface; + + // get width/height of original surface + const sal_uInt32 nSourceWidth(cairo_image_surface_get_width(mpCairoSurface)); + const sal_uInt32 nSourceHeight(cairo_image_surface_get_height(mpCairoSurface)); + + // zoomed in, need to stretch at paint, no pre-scale useful + if (nTargetWidth >= nSourceWidth || nTargetHeight >= nSourceHeight) + return mpCairoSurface; + + // calculate downscale factor. Only use ONE factor to get the diagonal + // MipMap, NOT the full MipMap field in X/Y for uneven factors in both dimensions + sal_uInt32 nFactor(1); + sal_uInt32 nW((nSourceWidth + 1) / 2); + sal_uInt32 nH((nSourceHeight + 1) / 2); + + while (nW > nTargetWidth && nW > nHalfMDSize && nH > nTargetHeight && nH > nHalfMDSize) + { + nW = (nW + 1) / 2; + nH = (nH + 1) / 2; + nFactor *= 2; + } + + if (1 == nFactor) + { + // original size *is* best binary size, use it + return mpCairoSurface; + } + + // go up one scale again + nW *= 2; + nH *= 2; + + // bail out if the multiplication for the key would overflow + if (nW >= SAL_MAX_UINT32 || nH >= SAL_MAX_UINT32) + return mpCairoSurface; + + // check if we have a downscaled version of required size + const sal_uInt64 key((nW * static_cast<sal_uInt64>(SAL_MAX_UINT32)) + nH); + auto isHit(maDownscaled.find(key)); + + // found -> return it + if (isHit != maDownscaled.end()) + return isHit->second; + + // create new surface in the targeted size + cairo_surface_t* pSurfaceTarget(cairo_surface_create_similar( + mpCairoSurface, cairo_surface_get_content(mpCairoSurface), nW, nH)); + + // made a version to scale self first with direct memory access. + // That worked well, but would've been hard to support + // CAIRO_FORMAT_A1 and similar (including bit shifting), so + // I decided to go with cairo itself - use CAIRO_FILTER_FAST or + // CAIRO_FILTER_GOOD though. Please modify as needed for + // performance/quality + cairo_t* cr = cairo_create(pSurfaceTarget); + const double fScaleX(static_cast<double>(nW) / static_cast<double>(nSourceWidth)); + const double fScaleY(static_cast<double>(nH) / static_cast<double>(nSourceHeight)); + + cairo_scale(cr, fScaleX, fScaleY); + cairo_set_source_surface(cr, mpCairoSurface, 0.0, 0.0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD); + cairo_paint(cr); + cairo_destroy(cr); + + // NOTE: Took out, until now not really needed + // need to set device_scale for downscale surfaces to get + // them handled correctly + // cairo_surface_set_device_scale(pSurfaceTarget, fScaleX, fScaleY); + + // add entry to cached entries + maDownscaled[key] = pSurfaceTarget; + + return pSurfaceTarget; + } + + bool isTrivial() const + { + if (nullptr == mpCairoSurface) + return true; + + const sal_uInt32 nSourceWidth(cairo_image_surface_get_width(mpCairoSurface)); + const sal_uInt32 nSourceHeight(cairo_image_surface_get_height(mpCairoSurface)); + + return nSourceWidth * nSourceHeight < nMinimalDiscreteSquareSizeToBuffer; + } +}; + +class SystemDependentData_CairoSurface : public basegfx::SystemDependentData +{ + // the CairoSurface holder + std::shared_ptr<CairoSurfaceHelper> mpCairoSurfaceHelper; + +public: + SystemDependentData_CairoSurface(const Bitmap& rBitmap) + : basegfx::SystemDependentData(Application::GetSystemDependentDataManager(), + basegfx::SDD_Type::SDDType_CairoSurface) + , mpCairoSurfaceHelper(std::make_shared<CairoSurfaceHelper>(rBitmap)) + { + } + + // read access + const std::shared_ptr<CairoSurfaceHelper>& getCairoSurfaceHelper() const + { + return mpCairoSurfaceHelper; + } + + virtual sal_Int64 estimateUsageInBytes() const override; +}; + +sal_Int64 SystemDependentData_CairoSurface::estimateUsageInBytes() const +{ + sal_Int64 nRetval(0); + + if (mpCairoSurfaceHelper) + { + cairo_surface_t* pSurface(mpCairoSurfaceHelper->getCairoSurface()); + const tools::Long nStride(cairo_image_surface_get_stride(pSurface)); + const tools::Long nHeight(cairo_image_surface_get_height(pSurface)); + + nRetval = nStride * nHeight; + + // if we do downscale, size will grow by 1/4 + 1/16 + 1/32 + ..., + // rough estimation just multiplies by 1.25 .. 1.33, should be good enough + // for estimation of buffer survival time + if (!bDisableDownScale) + { + nRetval = (nRetval * 5) / 4; + } + } + + return nRetval; +} + +std::shared_ptr<CairoSurfaceHelper> getOrCreateCairoSurfaceHelper(const Bitmap& rBitmap) +{ + const basegfx::SystemDependentDataHolder* pHolder(rBitmap.accessSystemDependentDataHolder()); + std::shared_ptr<SystemDependentData_CairoSurface> pSystemDependentData_CairoSurface; + + if (nullptr != pHolder) + { + // try to access SystemDependentDataHolder and buffered data + pSystemDependentData_CairoSurface + = std::static_pointer_cast<SystemDependentData_CairoSurface>( + pHolder->getSystemDependentData(basegfx::SDD_Type::SDDType_CairoSurface)); + } + + if (!pSystemDependentData_CairoSurface) + { + // create new SystemDependentData_CairoSurface + pSystemDependentData_CairoSurface + = std::make_shared<SystemDependentData_CairoSurface>(rBitmap); + + // only add if feasible + if (nullptr != pHolder + && !pSystemDependentData_CairoSurface->getCairoSurfaceHelper()->isTrivial() + && pSystemDependentData_CairoSurface->calculateCombinedHoldCyclesInSeconds() > 0) + { + basegfx::SystemDependentData_SharedPtr r2(pSystemDependentData_CairoSurface); + const_cast<basegfx::SystemDependentDataHolder*>(pHolder) + ->addOrReplaceSystemDependentData(r2); + } + } + + return pSystemDependentData_CairoSurface->getCairoSurfaceHelper(); +} + +// This bit-tweaking looping is unpleasant and unfortunate +void LuminanceToAlpha(cairo_surface_t* pMask) +{ + cairo_surface_flush(pMask); + + const sal_uInt32 nWidth(cairo_image_surface_get_width(pMask)); + const sal_uInt32 nHeight(cairo_image_surface_get_height(pMask)); + const sal_uInt32 nStride(cairo_image_surface_get_stride(pMask)); + + if (0 == nWidth || 0 == nHeight) + return; + + unsigned char* mask_surface_data(cairo_image_surface_get_data(pMask)); + + // change to unsigned 16bit and shifting. This is not much + // faster on modern processors due to nowadays good double/ + // float HW, but may also be used on smaller HW (ARM, ...). + // Since source is sal_uInt8 integer using double (see version + // before) is not required numerically either. + // scaling values are now put to a 256 entry lookup for R, G and B + // thus 768 bytes, so no multiplications have to happen. The values + // used to create these are (54+183+18 == 255): + // sal_uInt16 nR(0.2125 * 256.0); // -> 54.4 + // sal_uInt16 nG(0.7154 * 256.0); // -> 183.1424 + // sal_uInt16 nB(0.0721 * 256.0); // -> 18.4576 + // and the short loop (for nR, nG and nB resp.) like: + // for(unsigned short a(0); a < 256; a++) + // std::cout << ((a * nR) / 255) << ", "; + static constexpr std::array<sal_uInt8, 256> nRArray + = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, + 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, + 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, + 27, 28, 28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, + 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, + 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, + 41, 42, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 46, 46, + 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 50, 50, 50, 51, + 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, 53, 53, 53, 54 }; + static constexpr std::array<sal_uInt8, 256> nGArray + = { 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, + 11, 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, + 22, 23, 24, 25, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33, 33, + 34, 35, 35, 36, 37, 38, 38, 39, 40, 40, 41, 42, 43, 43, 44, 45, + 45, 46, 47, 48, 48, 49, 50, 50, 51, 52, 53, 53, 54, 55, 55, 56, + 57, 58, 58, 59, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, + 68, 69, 70, 71, 71, 72, 73, 73, 74, 75, 76, 76, 77, 78, 78, 79, + 80, 81, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 88, 89, 90, 91, + 91, 92, 93, 94, 94, 95, 96, 96, 97, 98, 99, 99, 100, 101, 101, 102, + 103, 104, 104, 105, 106, 106, 107, 108, 109, 109, 110, 111, 111, 112, 113, 114, + 114, 115, 116, 116, 117, 118, 119, 119, 120, 121, 122, 122, 123, 124, 124, 125, + 126, 127, 127, 128, 129, 129, 130, 131, 132, 132, 133, 134, 134, 135, 136, 137, + 137, 138, 139, 139, 140, 141, 142, 142, 143, 144, 144, 145, 146, 147, 147, 148, + 149, 149, 150, 151, 152, 152, 153, 154, 155, 155, 156, 157, 157, 158, 159, 160, + 160, 161, 162, 162, 163, 164, 165, 165, 166, 167, 167, 168, 169, 170, 170, 171, + 172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 180, 180, 181, 182, 183 }; + static constexpr std::array<sal_uInt8, 256> nBArray + = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18 }; + + for (sal_uInt32 y(0); y < nHeight; ++y) + { + unsigned char* pMaskPixelData = mask_surface_data + (nStride * y); + + for (sal_uInt32 x(0); x < nWidth; ++x) + { + // do not forget that we have pre-multiplied alpha + sal_uInt8 nAlpha(pMaskPixelData[SVP_CAIRO_ALPHA]); + + if (0 != nAlpha) + { + // get Luminance in range [0..255] + const sal_uInt8 nLum(nRArray[pMaskPixelData[SVP_CAIRO_RED]] + + nGArray[pMaskPixelData[SVP_CAIRO_GREEN]] + + nBArray[pMaskPixelData[SVP_CAIRO_BLUE]]); + + if (255 != nAlpha) + // remove pre-multiplied alpha (use existing VCL tooling) + nAlpha = vcl::bitmap::unpremultiply(nLum, nAlpha); + else + // already what we need + nAlpha = nLum; + + pMaskPixelData[SVP_CAIRO_ALPHA] = 255 - nAlpha; + } + + pMaskPixelData += 4; + } + } + + cairo_surface_mark_dirty(pMask); +} + +basegfx::B2DRange getDiscreteViewRange(cairo_t* pRT) +{ + double clip_x1, clip_x2, clip_y1, clip_y2; + cairo_save(pRT); + cairo_identity_matrix(pRT); + cairo_clip_extents(pRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2); + cairo_restore(pRT); + + return basegfx::B2DRange(basegfx::B2DPoint(clip_x1, clip_y1), + basegfx::B2DPoint(clip_x2, clip_y2)); +} + +bool checkCoordinateLimitWorkaroundNeededForUsedCairo() +{ + // setup surface and render context + cairo_surface_t* pSurface(cairo_image_surface_create(CAIRO_FORMAT_RGB24, 8, 8)); + if (!pSurface) + { + SAL_INFO( + "drawinglayer", + "checkCoordinateLimitWorkaroundNeededForUsedCairo: got no surface -> be pessimistic"); + return true; + } + + cairo_t* pRender(cairo_create(pSurface)); + if (!pRender) + { + SAL_INFO( + "drawinglayer", + "checkCoordinateLimitWorkaroundNeededForUsedCairo: got no render -> be pessimistic"); + cairo_surface_destroy(pSurface); + return true; + } + + // set basic values + cairo_set_antialias(pRender, CAIRO_ANTIALIAS_NONE); + cairo_set_fill_rule(pRender, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_operator(pRender, CAIRO_OPERATOR_OVER); + cairo_set_source_rgb(pRender, 1.0, 0.0, 0.0); + + // create a to-be rendered area centered at the fNumCairoMax + // spot and 8x8 discrete units in size + constexpr double fNumCairoMax(1 << 23); + const basegfx::B2DPoint aCenter(fNumCairoMax, fNumCairoMax); + const basegfx::B2DPoint aOffset(4, 4); + const basegfx::B2DRange aObject(aCenter - aOffset, aCenter + aOffset); + + // create transformation to render that to an area with + // range(0, 0, 8, 8) and set as transformation + const basegfx::B2DHomMatrix aObjectToView(basegfx::utils::createSourceRangeTargetRangeTransform( + aObject, basegfx::B2DRange(0, 0, 8, 8))); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aObjectToView.a(), aObjectToView.b(), aObjectToView.c(), + aObjectToView.d(), aObjectToView.e(), aObjectToView.f()); + cairo_set_matrix(pRender, &aMatrix); + + // get/create the path for an object exactly filling that area + cairo_new_path(pRender); + basegfx::B2DPolyPolygon aObjectPolygon(basegfx::utils::createPolygonFromRect(aObject)); + CairoPathHelper aPathHelper(aObjectPolygon); + cairo_append_path(pRender, aPathHelper.getCairoPath()); + + // render it and flush since we want to immediately inspect result + cairo_fill(pRender); + cairo_surface_flush(pSurface); + + // get access to pixel data + const sal_uInt32 nStride(cairo_image_surface_get_stride(pSurface)); + sal_uInt8* pStartPixelData(cairo_image_surface_get_data(pSurface)); + + // extract red value for pixels at (1,1) and (7,7) + sal_uInt8 aRedAt_1_1((pStartPixelData + (nStride * 1) + 1)[SVP_CAIRO_RED]); + sal_uInt8 aRedAt_6_6((pStartPixelData + (nStride * 6) + 6)[SVP_CAIRO_RED]); + + // cleanup + cairo_destroy(pRender); + cairo_surface_destroy(pSurface); + + // if cairo works or has no 24.8 internal format all pixels + // have to be red (255), thus workaround is needed if != + auto const needed = aRedAt_1_1 != aRedAt_6_6; + SAL_INFO("drawinglayer", "checkCoordinateLimitWorkaroundNeededForUsedCairo: " << needed); + return needed; +} +} + +namespace drawinglayer::processor2d +{ +void CairoPixelProcessor2D::onViewInformation2DChanged() +{ + // apply AntiAlias information to target device + cairo_set_antialias(mpRT, getViewInformation2D().getUseAntiAliasing() ? CAIRO_ANTIALIAS_DEFAULT + : CAIRO_ANTIALIAS_NONE); +} + +CairoPixelProcessor2D::CairoPixelProcessor2D( + const basegfx::BColorModifierStack& rBColorModifierStack, + const geometry::ViewInformation2D& rViewInformation, cairo_surface_t* pTarget) + : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(nullptr) + , maBColorModifierStack(rBColorModifierStack) + , mpOwnedSurface(nullptr) + , mpRT(nullptr) + , mbRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , mbRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) + , mnClipRecursionCount(0) + , mbCairoCoordinateLimitWorkaroundActive(false) +{ + // no target, nothing to initialize + if (nullptr == pTarget) + return; + + // create RenderTarget for full target + mpRT = cairo_create(pTarget); + + if (nullptr == mpRT) + // error, invalid + return; + + // initialize some basic used values/settings + cairo_set_antialias(mpRT, rViewInformation.getUseAntiAliasing() ? CAIRO_ANTIALIAS_DEFAULT + : CAIRO_ANTIALIAS_NONE); + cairo_set_fill_rule(mpRT, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_operator(mpRT, CAIRO_OPERATOR_OVER); + + // evaluate if CairoCoordinateLimitWorkaround is needed + evaluateCairoCoordinateLimitWorkaround(); +} + +CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, + tools::Long nWidthPixel, tools::Long nHeightPixel, + bool bUseRGBA) + : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(nullptr) + , maBColorModifierStack() + , mpOwnedSurface(nullptr) + , mpRT(nullptr) + , mbRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , mbRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) + , mnClipRecursionCount(0) + , mbCairoCoordinateLimitWorkaroundActive(false) +{ + if (nWidthPixel <= 0 || nHeightPixel <= 0) + // no size, invalid + return; + + mpOwnedSurface = cairo_image_surface_create(bUseRGBA ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, + nWidthPixel, nHeightPixel); + + if (nullptr == mpOwnedSurface) + // error, invalid + return; + + // create RenderTarget for full target + mpRT = cairo_create(mpOwnedSurface); + + if (nullptr == mpRT) + // error, invalid + return; + + // initialize some basic used values/settings + cairo_set_antialias(mpRT, rViewInformation.getUseAntiAliasing() ? CAIRO_ANTIALIAS_DEFAULT + : CAIRO_ANTIALIAS_NONE); + cairo_set_fill_rule(mpRT, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_operator(mpRT, CAIRO_OPERATOR_OVER); + + // evaluate if CairoCoordinateLimitWorkaround is needed + evaluateCairoCoordinateLimitWorkaround(); +} + +CairoPixelProcessor2D::CairoPixelProcessor2D(OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(&rOutputDevice) + , maBColorModifierStack() + , mpOwnedSurface(nullptr) + , mpRT(nullptr) + , mbRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , mbRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) + , mnClipRecursionCount(0) + , mbCairoCoordinateLimitWorkaroundActive(false) +{ + SystemGraphicsData aData(mpTargetOutputDevice->GetSystemGfxData()); + cairo_surface_t* pTarget(static_cast<cairo_surface_t*>(aData.pSurface)); + + // no target, nothing to initialize + if (nullptr == pTarget) + { + mpTargetOutputDevice = nullptr; + return; + } + + // get evtl. offsets if OutputDevice is e.g. a OUTDEV_WINDOW + // to evaluate if initial clip is needed + const tools::Long nOffsetPixelX(mpTargetOutputDevice->GetOutOffXPixel()); + const tools::Long nOffsetPixelY(mpTargetOutputDevice->GetOutOffYPixel()); + const tools::Long nWidthPixel(mpTargetOutputDevice->GetOutputWidthPixel()); + const tools::Long nHeightPixel(mpTargetOutputDevice->GetOutputHeightPixel()); + bool bClipNeeded(false); + + if (0 != nOffsetPixelX || 0 != nOffsetPixelY || 0 != nWidthPixel || 0 != nHeightPixel) + { + if (0 != nOffsetPixelX || 0 != nOffsetPixelY) + { + // if offset is used we need initial clip + bClipNeeded = true; + } + else + { + // no offset used, compare to real pixel size + const tools::Long nRealPixelWidth(cairo_image_surface_get_width(pTarget)); + const tools::Long nRealPixelHeight(cairo_image_surface_get_height(pTarget)); + + if (nRealPixelWidth != nWidthPixel || nRealPixelHeight != nHeightPixel) + { + // if size differs we need initial clip + bClipNeeded = true; + } + } + } + + if (bClipNeeded) + { + // Make use of the possibility to add an initial clip relative + // to the 'real' pixel dimensions of the target surface. This is e.g. + // needed here due to the existence of 'virtual' target surfaces that + // internally use an offset and limited pixel size, mainly used for + // UI elements. + // let the CairoPixelProcessor2D do this, it has internal, + // system-specific possibilities to do that in an elegant and + // efficient way (using cairo_surface_create_for_rectangle). + mpOwnedSurface = cairo_surface_create_for_rectangle(pTarget, nOffsetPixelX, nOffsetPixelY, + nWidthPixel, nHeightPixel); + + if (nullptr == mpOwnedSurface) + { + // error, invalid + mpTargetOutputDevice = nullptr; + return; + } + + mpRT = cairo_create(mpOwnedSurface); + } + else + { + // create RenderTarget for full target + mpRT = cairo_create(pTarget); + } + + if (nullptr == mpRT) + { + // error, invalid + mpTargetOutputDevice = nullptr; + return; + } + + // initialize some basic used values/settings + cairo_set_antialias(mpRT, rViewInformation.getUseAntiAliasing() ? CAIRO_ANTIALIAS_DEFAULT + : CAIRO_ANTIALIAS_NONE); + cairo_set_fill_rule(mpRT, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_operator(mpRT, CAIRO_OPERATOR_OVER); + + // prepare output directly to pixels + mpTargetOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpTargetOutputDevice->SetMapMode(); + + // evaluate if CairoCoordinateLimitWorkaround is needed + evaluateCairoCoordinateLimitWorkaround(); +} + +CairoPixelProcessor2D::~CairoPixelProcessor2D() +{ + if (nullptr != mpTargetOutputDevice) // restore MapMode + mpTargetOutputDevice->Pop(); + if (nullptr != mpRT) + cairo_destroy(mpRT); + if (nullptr != mpOwnedSurface) + cairo_surface_destroy(mpOwnedSurface); +} + +Bitmap CairoPixelProcessor2D::extractBitmap() const +{ + // default is empty Bitmap + Bitmap aRetval; + + if (nullptr == mpRT) + // no RenderContext, not valid + return aRetval; + + cairo_surface_t* pSource(cairo_get_target(mpRT)); + if (nullptr == pSource) + // no surface, not valid + return aRetval; + + // check pixel sizes + const sal_uInt32 nWidth(cairo_image_surface_get_width(pSource)); + const sal_uInt32 nHeight(cairo_image_surface_get_height(pSource)); + if (0 == nWidth || 0 == nHeight) + // no content, not valid + return aRetval; + + // check format + const cairo_format_t aFormat(cairo_image_surface_get_format(pSource)); + if (CAIRO_FORMAT_ARGB32 != aFormat && CAIRO_FORMAT_RGB24 != aFormat) + // we for now only support ARGB32 and RGB24, format not supported, not valid + return aRetval; + + // ensure surface read access, wer need CAIRO_SURFACE_TYPE_IMAGE + cairo_surface_t* pReadSource(pSource); + + if (CAIRO_SURFACE_TYPE_IMAGE != cairo_surface_get_type(pReadSource)) + { + // create mapping for read access to source + pReadSource = cairo_surface_map_to_image(pReadSource, nullptr); + } + + // prepare VCL/Bitmap stuff + const Size aBitmapSize(nWidth, nHeight); + const bool bHasAlpha(CAIRO_FORMAT_ARGB32 == aFormat); + Bitmap aBitmap(aBitmapSize, bHasAlpha ? vcl::PixelFormat::N32_BPP : vcl::PixelFormat::N24_BPP); + BitmapWriteAccess aAccess(aBitmap); + if (!aAccess) + { + SAL_WARN("drawinglayer", "Could not create image, likely too large, size= " << aBitmapSize); + return aRetval; + } + + // prepare cairo stuff + const sal_uInt32 nStride(cairo_image_surface_get_stride(pReadSource)); + unsigned char* pStartPixelData(cairo_image_surface_get_data(pReadSource)); + + // separate loops for bHasAlpha so that we have *no* branch in the + // loops itself + if (bHasAlpha) + { + for (sal_uInt32 y(0); y < nHeight; ++y) + { + // prepare scanline + unsigned char* pPixelData(pStartPixelData + (nStride * y)); + Scanline pWriteRGBA = aAccess.GetScanline(y); + + for (sal_uInt32 x(0); x < nWidth; ++x) + { + // RGBA: Do not forget: it's pre-multiplied + sal_uInt8 nAlpha(pPixelData[SVP_CAIRO_ALPHA]); + aAccess.SetPixelOnData( + pWriteRGBA, x, + BitmapColor( + ColorAlpha, vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_RED], nAlpha), + vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_GREEN], nAlpha), + vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_BLUE], nAlpha), nAlpha)); + pPixelData += 4; + } + } + } + else + { + for (sal_uInt32 y(0); y < nHeight; ++y) + { + // prepare scanline + unsigned char* pPixelData(pStartPixelData + (nStride * y)); + Scanline pWriteRGB = aAccess.GetScanline(y); + + for (sal_uInt32 x(0); x < nWidth; ++x) + { + aAccess.SetPixelOnData(pWriteRGB, x, + BitmapColor(pPixelData[SVP_CAIRO_RED], + pPixelData[SVP_CAIRO_GREEN], + pPixelData[SVP_CAIRO_BLUE])); + pPixelData += 4; + } + } + } + + // construct and return Bitmap + aRetval = std::move(aBitmap); + + if (pReadSource != pSource) + { + // cleanup mapping for read/write access to source + cairo_surface_unmap_image(pSource, pReadSource); + } + + return aRetval; +} + +void CairoPixelProcessor2D::processBitmapPrimitive2D( + const primitive2d::BitmapPrimitive2D& rBitmapCandidate) +{ + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + const bool bDrawModeFlagsUsed(aDrawModeFlags & BITMAP); + + if (bDrawModeFlagsUsed) + { + // if DrawModeFlags for Bitmap are used, encapsulate with + // corresponding BColorModifier + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + } + + paintBitmapAlpha(rBitmapCandidate.getBitmap(), rBitmapCandidate.getTransform()); + + if (bDrawModeFlagsUsed) + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::paintBitmapAlpha(const Bitmap& rBitmap, + const basegfx::B2DHomMatrix& rTransform, + double fTransparency) +{ + // transparency invalid or completely transparent, done + if (fTransparency < 0.0 || fTransparency >= 1.0) + { + return; + } + + // check if graphic content is inside discrete local ViewPort + const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation() * rTransform); + + if (!rDiscreteViewPort.isEmpty()) + { + basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + + aUnitRange.transform(aLocalTransform); + + if (!aUnitRange.overlaps(rDiscreteViewPort)) + { + // content is outside discrete local ViewPort + return; + } + } + + Bitmap aBitmap(rBitmap); + + if (aBitmap.IsEmpty() || aBitmap.GetSizePixel().IsEmpty()) + { + // no pixel data, done + return; + } + + // work with dimensions in discrete target pixels to use evtl. MipMap pre-scale + const tools::Long nDestWidth((aLocalTransform * basegfx::B2DVector(1.0, 0.0)).getLength()); + const tools::Long nDestHeight((aLocalTransform * basegfx::B2DVector(0.0, 1.0)).getLength()); + + // tdf#167831 check for output size, may have zero discrete dimension in X and/or Y + if (0 == nDestWidth || 0 == nDestHeight) + { + // it has and is thus invisible + return; + } + + if (maBColorModifierStack.count()) + { + // need to apply ColorModifier to Bitmap data + aBitmap = aBitmap.Modify(maBColorModifierStack); + + if (aBitmap.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aModifiedColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + + // use unit geometry as fallback object geometry. Do *not* + // transform, the below used method will use the already + // correctly initialized local ViewInformation + const basegfx::B2DPolygon& aPolygon(basegfx::utils::createUnitPolygon()); + + // draw directly, done + paintPolyPolygonRGBA(basegfx::B2DPolyPolygon(aPolygon), aModifiedColor, fTransparency); + + return; + } + } + + // access or create cairo bitmap data + std::shared_ptr<CairoSurfaceHelper> aCairoSurfaceHelper(getOrCreateCairoSurfaceHelper(aBitmap)); + if (!aCairoSurfaceHelper) + { + SAL_WARN("drawinglayer", "SDPRCairo: No SurfaceHelper from Bitmap (!)"); + return; + } + + cairo_surface_t* pTarget(aCairoSurfaceHelper->getCairoSurface(nDestWidth, nDestHeight)); + if (nullptr == pTarget) + { + SAL_WARN("drawinglayer", "SDPRCairo: No CairoSurface from Bitmap SurfaceHelper (!)"); + return; + } + + cairo_save(mpRT); + + // set linear transformation - no fAAOffset for bitmap data + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), + aLocalTransform.d(), aLocalTransform.e(), aLocalTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + + static bool bRenderTransformationBounds(false); + if (bRenderTransformationBounds) + { + cairo_set_source_rgba(mpRT, 1, 0, 0, 0.8); + impl_cairo_set_hairline(mpRT, getViewInformation2D(), + isCairoCoordinateLimitWorkaroundActive()); + cairo_rectangle(mpRT, 0, 0, 1, 1); + cairo_stroke(mpRT); + } + + cairo_set_source_surface(mpRT, pTarget, 0, 0); + + // get the pattern created by cairo_set_source_surface and + // it's transformation + cairo_pattern_t* sourcepattern = cairo_get_source(mpRT); + cairo_pattern_get_matrix(sourcepattern, &aMatrix); + + // RGBA sources overlap the unit geometry range, slightly, + // to see that activate bRenderTransformationBounds and + // insert a ARGB image, zoom to the borders. Seems to be half + // a pixel. Very good to demonstrate: 8x1 pixel, some + // transparent. + // Also errors with images 1 pixel wide/high, e.g. insert + // RGBA 8x1, 1x8 to see (and deactivate fix below). It also + // depends on the used filter, see comment below at + // cairo_pattern_set_filter. Found also errors with more + // than one pixel, so cannot use as criteria. + // This effect is also visible in the left/right/bottom/top + // page shadows, these DO use 8x1/1x8 images which led me to + // that problem. I double-checked that these *are* correctly + // defined, that is not the problem. + // Decided now to use clipping always. That again is + // simple (we are in unit coordinates) + cairo_rectangle(mpRT, 0, 0, 1, 1); + cairo_clip(mpRT); + cairo_matrix_scale(&aMatrix, cairo_image_surface_get_width(pTarget), + cairo_image_surface_get_height(pTarget)); + + // The alternative wpuld be: resize/scale it SLIGHTLY to force + // that half pixel overlap to be inside the unit range. + // That makes the error disappear, so no clip needed, but + // SLIGHTLY smaller. Keeping this code if someone might have + // to finetune this later for reference. + // + // cairo_matrix_init_scale(&aMatrix, nWidth + 1, nHeight + 1); + // cairo_matrix_translate(&aMatrix, -0.5 / (nWidth + 1), -0.5 / (nHeight + 1)); + + // The error/effect described above also is connected to the + // filter used, so I checked the filter modes available + // in Cairo: + // + // CAIRO_FILTER_FAST: okay, small errors, sometimes stretching some pixels + // CAIRO_FILTER_GOOD: stretching error + // CAIRO_FILTER_BEST: okay, small errors + // CAIRO_FILTER_NEAREST: similar to CAIRO_FILTER_FAST + // CAIRO_FILTER_BILINEAR: similar to CAIRO_FILTER_GOOD + // CAIRO_FILTER_GAUSSIAN: same as CAIRO_FILTER_GOOD/CAIRO_FILTER_BILINEAR, should + // not be used anyways (see docs) + // + // CAIRO_FILTER_GOOD seems to be the default anyways, but set it + // to be on the safe side + cairo_pattern_set_filter(sourcepattern, CAIRO_FILTER_GOOD); + + // also set extend to CAIRO_EXTEND_PAD, else the outside of the + // bitmap is guessed as COL_BLACK and the filtering would blend + // against COL_BLACK what might give strange gray lines at borders + // of white-on-white bitmaps (used e.g. when painting controls). + // NOTE: CAIRO_EXTEND_REPEAT also works with clipping and might be + // broader supported by Cairo implementations + cairo_pattern_set_extend(sourcepattern, CAIRO_EXTEND_PAD); + + cairo_pattern_set_matrix(sourcepattern, &aMatrix); + + // paint bitmap data, evtl. with additional alpha channel + if (!basegfx::fTools::equalZero(fTransparency)) + cairo_paint_with_alpha(mpRT, 1.0 - fTransparency); + else + cairo_paint(mpRT); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processPointArrayPrimitive2D( + const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate) +{ + const std::vector<basegfx::B2DPoint>& rPositions(rPointArrayCandidate.getPositions()); + + if (rPositions.empty()) + { + // no geometry, done + return; + } + + cairo_save(mpRT); + + // determine & set color + basegfx::BColor aPointColor(getLineColor(rPointArrayCandidate.getRGBColor())); + aPointColor = maBColorModifierStack.getModifiedColor(aPointColor); + cairo_set_source_rgb(mpRT, aPointColor.getRed(), aPointColor.getGreen(), aPointColor.getBlue()); + + // To really paint a single pixel I found nothing better than + // switch off AA and draw a pixel-aligned rectangle + const cairo_antialias_t eOldAAMode(cairo_get_antialias(mpRT)); + cairo_set_antialias(mpRT, CAIRO_ANTIALIAS_NONE); + + for (auto const& pos : rPositions) + { + const basegfx::B2DPoint aDiscretePos(getViewInformation2D().getObjectToViewTransformation() + * pos); + const double fX(ceil(aDiscretePos.getX())); + const double fY(ceil(aDiscretePos.getY())); + + cairo_rectangle(mpRT, fX, fY, 1, 1); + cairo_fill(mpRT); + } + + cairo_set_antialias(mpRT, eOldAAMode); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processPolygonHairlinePrimitive2D( + const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) +{ + const basegfx::B2DPolygon& rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon()); + + if (!rPolygon.count()) + { + // no geometry, done + return; + } + + cairo_save(mpRT); + + // determine & set color + basegfx::BColor aHairlineColor(getLineColor(rPolygonHairlinePrimitive2D.getBColor())); + aHairlineColor = maBColorModifierStack.getModifiedColor(aHairlineColor); + cairo_set_source_rgb(mpRT, aHairlineColor.getRed(), aHairlineColor.getGreen(), + aHairlineColor.getBlue()); + + // set LineWidth, use Cairo's special cairo_set_hairline + impl_cairo_set_hairline(mpRT, getViewInformation2D(), isCairoCoordinateLimitWorkaroundActive()); + + if (isCairoCoordinateLimitWorkaroundActive()) + { + // need to fallback to paint in view coordinates, unfortunately + // need to transform self (cairo will do it wrong in this coordinate + // space), so no need to try to buffer + cairo_new_path(mpRT); + basegfx::B2DPolygon aAdaptedPolygon(rPolygon); + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + aAdaptedPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(fAAOffset, fAAOffset) + * getViewInformation2D().getObjectToViewTransformation()); + cairo_identity_matrix(mpRT); + addB2DPolygonToPathGeometry(mpRT, aAdaptedPolygon); + cairo_stroke(mpRT); + } + else + { + // set linear transformation. use own, prepared, re-usable + // ObjectToViewTransformation and PolyPolygon data and let + // cairo do the transformations + cairo_matrix_t aMatrix; + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e() + fAAOffset, + rObjectToView.f() + fAAOffset); + cairo_set_matrix(mpRT, &aMatrix); + + // get PathGeometry & paint it + cairo_new_path(mpRT); + getOrCreatePathGeometry(mpRT, rPolygon, getViewInformation2D(), + getViewInformation2D().getUseAntiAliasing()); + cairo_stroke(mpRT); + } + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processPolyPolygonColorPrimitive2D( + const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) +{ + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::NoFill) + // NoFill wanted, done + return; + + const basegfx::BColor aFillColor(getFillColor(rPolyPolygonColorPrimitive2D.getBColor())); + paintPolyPolygonRGBA(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon(), aFillColor); +} + +void CairoPixelProcessor2D::paintPolyPolygonRGBA(const basegfx::B2DPolyPolygon& rPolyPolygon, + const basegfx::BColor& rColor, + double fTransparency) +{ + // transparency invalid or completely transparent, done + if (fTransparency < 0.0 || fTransparency >= 1.0) + { + return; + } + + const sal_uInt32 nCount(rPolyPolygon.count()); + + if (!nCount) + { + // no geometry, done + return; + } + + cairo_save(mpRT); + + // determine & set color + const basegfx::BColor aFillColor(maBColorModifierStack.getModifiedColor(rColor)); + + if (!basegfx::fTools::equalZero(fTransparency)) + cairo_set_source_rgba(mpRT, aFillColor.getRed(), aFillColor.getGreen(), + aFillColor.getBlue(), 1.0 - fTransparency); + else + cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), + aFillColor.getBlue()); + + if (isCairoCoordinateLimitWorkaroundActive()) + { + // need to fallback to paint in view coordinates, unfortunately + // need to transform self (cairo will do it wrong in this coordinate + // space), so no need to try to buffer + cairo_new_path(mpRT); + basegfx::B2DPolyPolygon aAdaptedPolyPolygon(rPolyPolygon); + aAdaptedPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + cairo_identity_matrix(mpRT); + for (const auto& rPolygon : aAdaptedPolyPolygon) + addB2DPolygonToPathGeometry(mpRT, rPolygon); + cairo_fill(mpRT); + } + else + { + // set linear transformation. use own, prepared, re-usable + // ObjectToViewTransformation and PolyPolygon data and let + // cairo do the transformations + cairo_matrix_t aMatrix; + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e(), rObjectToView.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // get PathGeometry & paint it + cairo_new_path(mpRT); + getOrCreateFillGeometry(mpRT, rPolyPolygon); + cairo_fill(mpRT); + } + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processTransparencePrimitive2D( + const primitive2d::TransparencePrimitive2D& rTransCandidate) +{ + if (rTransCandidate.getChildren().empty()) + { + // no content, done + return; + } + + if (rTransCandidate.getTransparence().empty()) + { + // no mask (so nothing visible), done + return; + } + + // calculate visible range, create only for that range + basegfx::B2DRange aDiscreteRange( + rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); + aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); + basegfx::B2DRange aVisibleRange(aDiscreteRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); + + if (aVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + cairo_save(mpRT); + + // tdf#166734 need to expand to full pixels due to pre-rendering + // will use discrete pixels/top-left position + aVisibleRange.expand( + basegfx::B2DPoint(floor(aVisibleRange.getMinX()), floor(aVisibleRange.getMinY()))); + aVisibleRange.expand( + basegfx::B2DPoint(ceil(aVisibleRange.getMaxX()), ceil(aVisibleRange.getMaxY()))); + + // create embedding transformation for sub-surface + const basegfx::B2DHomMatrix aEmbedTransform(basegfx::utils::createTranslateB2DHomMatrix( + -aVisibleRange.getMinX(), -aVisibleRange.getMinY())); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setViewTransformation(aEmbedTransform + * getViewInformation2D().getViewTransformation()); + + // draw mask to temporary surface + cairo_surface_t* pTarget(cairo_get_target(mpRT)); + const double fContainedWidth(aVisibleRange.getWidth()); + const double fContainedHeight(aVisibleRange.getHeight()); + cairo_surface_t* pMask(cairo_surface_create_similar_image(pTarget, CAIRO_FORMAT_ARGB32, + fContainedWidth, fContainedHeight)); + CairoPixelProcessor2D aMaskRenderer(getBColorModifierStack(), aViewInformation2D, pMask); + aMaskRenderer.process(rTransCandidate.getTransparence()); + + // convert mask to something cairo can use + LuminanceToAlpha(pMask); + + // draw content to temporary surface + cairo_surface_t* pContent(cairo_surface_create_similar( + pTarget, cairo_surface_get_content(pTarget), fContainedWidth, fContainedHeight)); + CairoPixelProcessor2D aContent(getBColorModifierStack(), aViewInformation2D, pContent); + aContent.process(rTransCandidate.getChildren()); + + // munge the temporary surfaces to our target surface + cairo_set_source_surface(mpRT, pContent, aVisibleRange.getMinX(), aVisibleRange.getMinY()); + cairo_mask_surface(mpRT, pMask, aVisibleRange.getMinX(), aVisibleRange.getMinY()); + + // cleanup temporary surfaces + cairo_surface_destroy(pContent); + cairo_surface_destroy(pMask); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processInvertPrimitive2D( + const primitive2d::InvertPrimitive2D& rInvertCandidate) +{ + if (rInvertCandidate.getChildren().empty()) + { + // no content, done + return; + } + + // calculate visible range, create only for that range + basegfx::B2DRange aDiscreteRange( + rInvertCandidate.getChildren().getB2DRange(getViewInformation2D())); + aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); + basegfx::B2DRange aVisibleRange(aDiscreteRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); + + if (aVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + cairo_save(mpRT); + + // tdf#166734 need to expand to full pixels due to pre-rendering + // will use discrete pixels/top-left position + aVisibleRange.expand( + basegfx::B2DPoint(floor(aVisibleRange.getMinX()), floor(aVisibleRange.getMinY()))); + aVisibleRange.expand( + basegfx::B2DPoint(ceil(aVisibleRange.getMaxX()), ceil(aVisibleRange.getMaxY()))); + + // create embedding transformation for sub-surface + const basegfx::B2DHomMatrix aEmbedTransform(basegfx::utils::createTranslateB2DHomMatrix( + -aVisibleRange.getMinX(), -aVisibleRange.getMinY())); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setViewTransformation(aEmbedTransform + * getViewInformation2D().getViewTransformation()); + + // draw sub-content to temporary surface + cairo_surface_t* pTarget(cairo_get_target(mpRT)); + const double fContainedWidth(aVisibleRange.getWidth()); + const double fContainedHeight(aVisibleRange.getHeight()); + cairo_surface_t* pContent(cairo_surface_create_similar_image( + pTarget, CAIRO_FORMAT_ARGB32, fContainedWidth, fContainedHeight)); + CairoPixelProcessor2D aContent(getBColorModifierStack(), aViewInformation2D, pContent); + aContent.process(rInvertCandidate.getChildren()); + cairo_surface_flush(pContent); + + // decide if to use builtin or create XOR yourself + // NOTE: not using and doing self is closer to what the + // current default does, so keep it + static bool bUseBuiltinXOR(false); + + if (bUseBuiltinXOR) + { + // draw XOR to target using Cairo Operator CAIRO_OPERATOR_XOR + cairo_set_source_surface(mpRT, pContent, aVisibleRange.getMinX(), aVisibleRange.getMinY()); + cairo_rectangle(mpRT, aVisibleRange.getMinX(), aVisibleRange.getMinY(), + aVisibleRange.getWidth(), aVisibleRange.getHeight()); + cairo_set_operator(mpRT, CAIRO_OPERATOR_XOR); + cairo_fill(mpRT); + } + else + { + // get read/write access to target - XOR unfortunately needs that + cairo_surface_t* pRenderTarget(pTarget); + + if (CAIRO_SURFACE_TYPE_IMAGE != cairo_surface_get_type(pRenderTarget)) + { + // create mapping for read/write access to pRenderTarget + pRenderTarget = cairo_surface_map_to_image(pRenderTarget, nullptr); + } + + // iterate over pre-rendered pContent (call it Front) + const sal_uInt32 nFrontWidth(cairo_image_surface_get_width(pContent)); + const sal_uInt32 nFrontHeight(cairo_image_surface_get_height(pContent)); + const sal_uInt32 nFrontStride(cairo_image_surface_get_stride(pContent)); + unsigned char* pFrontDataRoot(cairo_image_surface_get_data(pContent)); + + // in parallel, iterate over original data (call it Back) + const sal_uInt32 nBackOffX(aVisibleRange.getMinX()); + const sal_uInt32 nBackOffY(aVisibleRange.getMinY()); + const sal_uInt32 nBackStride(cairo_image_surface_get_stride(pRenderTarget)); + unsigned char* pBackDataRoot(cairo_image_surface_get_data(pRenderTarget)); + const bool bBackPreMultiply(CAIRO_FORMAT_ARGB32 + == cairo_image_surface_get_format(pRenderTarget)); + + if (nullptr != pFrontDataRoot && nullptr != pBackDataRoot) + { + for (sal_uInt32 y(0); y < nFrontHeight; ++y) + { + // get mem locations + unsigned char* pFrontData(pFrontDataRoot + (nFrontStride * y)); + unsigned char* pBackData(pBackDataRoot + (nBackStride * (y + nBackOffY)) + + (nBackOffX * 4)); + + // added advance mem to for-expression to be able to continue calls inside + for (sal_uInt32 x(0); x < nFrontWidth; ++x, pBackData += 4, pFrontData += 4) + { + // do not forget pre-multiply. Use 255 for non-premultiplied to + // not have to do if not needed + const sal_uInt8 nBackAlpha(bBackPreMultiply ? pBackData[SVP_CAIRO_ALPHA] : 255); + + // change will only be visible in back/target when not fully transparent + if (0 == nBackAlpha) + continue; + + // do not forget pre-multiply -> need to get both alphas. Use 255 + // for non-premultiplied to not have to do if not needed + const sal_uInt8 nFrontAlpha(pFrontData[SVP_CAIRO_ALPHA]); + + // only something to do if source is not fully transparent + if (0 == nFrontAlpha) + continue; + + sal_uInt8 nFrontB(pFrontData[SVP_CAIRO_BLUE]); + sal_uInt8 nFrontG(pFrontData[SVP_CAIRO_GREEN]); + sal_uInt8 nFrontR(pFrontData[SVP_CAIRO_RED]); + + if (255 != nFrontAlpha) + { + // get front color (Front is always CAIRO_FORMAT_ARGB32 and + // thus pre-multiplied) + nFrontB = vcl::bitmap::unpremultiply(nFrontB, nFrontAlpha); + nFrontG = vcl::bitmap::unpremultiply(nFrontG, nFrontAlpha); + nFrontR = vcl::bitmap::unpremultiply(nFrontR, nFrontAlpha); + } + + sal_uInt8 nBackB(pBackData[SVP_CAIRO_BLUE]); + sal_uInt8 nBackG(pBackData[SVP_CAIRO_GREEN]); + sal_uInt8 nBackR(pBackData[SVP_CAIRO_RED]); + + if (255 != nBackAlpha) + { + // get back color if bBackPreMultiply (aka 255) + nBackB = vcl::bitmap::unpremultiply(nBackB, nBackAlpha); + nBackG = vcl::bitmap::unpremultiply(nBackG, nBackAlpha); + nBackR = vcl::bitmap::unpremultiply(nBackR, nBackAlpha); + } + + // create XOR r,g,b + const sal_uInt8 b(nFrontB ^ nBackB); + const sal_uInt8 g(nFrontG ^ nBackG); + const sal_uInt8 r(nFrontR ^ nBackR); + + // write back directly to pBackData/target + if (255 == nBackAlpha) + { + pBackData[SVP_CAIRO_BLUE] = b; + pBackData[SVP_CAIRO_GREEN] = g; + pBackData[SVP_CAIRO_RED] = r; + } + else + { + // additionally premultiply if bBackPreMultiply (aka 255) + pBackData[SVP_CAIRO_BLUE] = vcl::bitmap::premultiply(b, nBackAlpha); + pBackData[SVP_CAIRO_GREEN] = vcl::bitmap::premultiply(g, nBackAlpha); + pBackData[SVP_CAIRO_RED] = vcl::bitmap::premultiply(r, nBackAlpha); + } + } + } + + cairo_surface_mark_dirty(pRenderTarget); + } + + if (pRenderTarget != pTarget) + { + // cleanup mapping for read/write access to target + cairo_surface_unmap_image(pTarget, pRenderTarget); + } + } + + // cleanup temporary surface + cairo_surface_destroy(pContent); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processMaskPrimitive2D( + const primitive2d::MaskPrimitive2D& rMaskCandidate) +{ + if (rMaskCandidate.getChildren().empty()) + { + // no content, done + return; + } + + const basegfx::B2DPolyPolygon& rMask(rMaskCandidate.getMask()); + + if (!rMask.count()) + { + // no mask (so nothing inside), done + return; + } + + // calculate visible range + basegfx::B2DRange aMaskRange(rMask.getB2DRange()); + aMaskRange.transform(getViewInformation2D().getObjectToViewTransformation()); + if (!getDiscreteViewRange(mpRT).overlaps(aMaskRange)) + { + // not visible, done + return; + } + + cairo_save(mpRT); + + if (isCairoCoordinateLimitWorkaroundActive()) + { + // need to fallback to paint in view coordinates, unfortunately + // need to transform self (cairo will do it wrong in this coordinate + // space), so no need to try to buffer + cairo_new_path(mpRT); + basegfx::B2DPolyPolygon aAdaptedPolyPolygon(rMask); + aAdaptedPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); + for (const auto& rPolygon : aAdaptedPolyPolygon) + addB2DPolygonToPathGeometry(mpRT, rPolygon); + + // clip to this mask + cairo_clip(mpRT); + } + else + { + // set linear transformation for applying mask. use no fAAOffset for mask + cairo_matrix_t aMatrix; + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e(), rObjectToView.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // create path geometry and put mask as path + cairo_new_path(mpRT); + getOrCreateFillGeometry(mpRT, rMask); + + // clip to this mask + cairo_clip(mpRT); + + // reset transformation to not have it set when processing + // child content below (was only used to set clip path) + cairo_identity_matrix(mpRT); + } + + // process sub-content (that shall be masked) + mnClipRecursionCount++; + process(rMaskCandidate.getChildren()); + mnClipRecursionCount--; + + cairo_restore(mpRT); + + if (0 == mnClipRecursionCount) + { + // for *some* reason Cairo seems to have problems using cairo_clip + // recursively, in combination with cairo_save/cairo_restore. I think + // it *should* work as used here, see + // https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-clip + // where this combination is explicitly mentioned/explained. It may + // just be a error in cairo, too (?). + // The error is that without that for some reason the last clip is not + // restored but *stays*, so e.g. when having a shape filled with + // 'tux.svg' and an ellipse overlapping in front, suddenly (but not + // always?) the ellipse gets 'clipped' against the shape filled with + // the tux graphic. + // What helps is to count the clip recursion for each incarnation of + // CairoPixelProcessor2D/cairo_t used and call/use cairo_reset_clip + // when last clip is left. + cairo_reset_clip(mpRT); + } +} + +void CairoPixelProcessor2D::processModifiedColorPrimitive2D( + const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate) +{ + // standard implementation + if (!rModifiedCandidate.getChildren().empty()) + { + maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); + process(rModifiedCandidate.getChildren()); + maBColorModifierStack.pop(); + } +} + +void CairoPixelProcessor2D::processTransformPrimitive2D( + const primitive2d::TransformPrimitive2D& rTransformCandidate) +{ + // standard implementation + // remember current transformation and ViewInformation + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for local ViewInformation2D + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); + + // process content + process(rTransformCandidate.getChildren()); + + // restore transformations + setViewInformation2D(aLastViewInformation2D); +} + +void CairoPixelProcessor2D::processUnifiedTransparencePrimitive2D( + const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate) +{ + if (rTransCandidate.getChildren().empty()) + { + // no content, done + return; + } + + if (0.0 == rTransCandidate.getTransparence()) + { + // not transparent at all, use content + process(rTransCandidate.getChildren()); + return; + } + + if (rTransCandidate.getTransparence() < 0.0 || rTransCandidate.getTransparence() > 1.0) + { + // invalid transparence, done + return; + } + + // calculate visible range, create only for that range + basegfx::B2DRange aDiscreteRange( + rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); + aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation()); + basegfx::B2DRange aVisibleRange(aDiscreteRange); + aVisibleRange.intersect(getDiscreteViewRange(mpRT)); + + if (aVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + cairo_save(mpRT); + + // tdf#166734 need to expand to full pixels due to pre-rendering + // will use discrete pixels/top-left position + aVisibleRange.expand( + basegfx::B2DPoint(floor(aVisibleRange.getMinX()), floor(aVisibleRange.getMinY()))); + aVisibleRange.expand( + basegfx::B2DPoint(ceil(aVisibleRange.getMaxX()), ceil(aVisibleRange.getMaxY()))); + + // create embedding transformation for sub-surface + const basegfx::B2DHomMatrix aEmbedTransform(basegfx::utils::createTranslateB2DHomMatrix( + -aVisibleRange.getMinX(), -aVisibleRange.getMinY())); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setViewTransformation(aEmbedTransform + * getViewInformation2D().getViewTransformation()); + + // draw content to temporary surface + cairo_surface_t* pTarget(cairo_get_target(mpRT)); + const double fContainedWidth(aVisibleRange.getWidth()); + const double fContainedHeight(aVisibleRange.getHeight()); + cairo_surface_t* pContent(cairo_surface_create_similar( + pTarget, cairo_surface_get_content(pTarget), fContainedWidth, fContainedHeight)); + CairoPixelProcessor2D aContent(getBColorModifierStack(), aViewInformation2D, pContent); + aContent.process(rTransCandidate.getChildren()); + + // paint temporary surface to target with fixed transparence + cairo_set_source_surface(mpRT, pContent, aVisibleRange.getMinX(), aVisibleRange.getMinY()); + cairo_paint_with_alpha(mpRT, 1.0 - rTransCandidate.getTransparence()); + + // cleanup temporary surface + cairo_surface_destroy(pContent); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processMarkerArrayPrimitive2D( + const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate) +{ + const std::vector<basegfx::B2DPoint>& rPositions(rMarkerArrayCandidate.getPositions()); + + if (rPositions.empty()) + { + // no geometry, done + return; + } + + const Bitmap& rMarker(rMarkerArrayCandidate.getMarker()); + + if (rMarker.IsEmpty()) + { + // no marker defined, done + return; + } + + // prepare Marker's Bitmap + Bitmap aBitmap(rMarkerArrayCandidate.getMarker()); + + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + if (aDrawModeFlags & BITMAP) + { + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + + // need to apply ColorModifier to Bitmap data + aBitmap = aBitmap.Modify(maBColorModifierStack); + + if (aBitmap.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aReplacementColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + Bitmap aBitmap2(rMarker.GetSizePixel(), vcl::PixelFormat::N24_BPP); + aBitmap2.Erase(Color(aReplacementColor)); + + if (rMarker.HasAlpha()) + aBitmap = Bitmap(aBitmap2, rMarker.CreateAlphaMask()); + else + aBitmap = std::move(aBitmap2); + } + + maBColorModifierStack.pop(); + } + + // access or create cairo bitmap data + std::shared_ptr<CairoSurfaceHelper> aCairoSurfaceHelper(getOrCreateCairoSurfaceHelper(aBitmap)); + if (!aCairoSurfaceHelper) + { + SAL_WARN("drawinglayer", "SDPRCairo: No SurfaceHelper from Bitmap (!)"); + return; + } + + // do not use dimensions, these are usually small instances + cairo_surface_t* pTarget(aCairoSurfaceHelper->getCairoSurface()); + if (nullptr == pTarget) + { + SAL_WARN("drawinglayer", "SDPRCairo: No CairoSurface from Bitmap SurfaceHelper (!)"); + return; + } + + const sal_uInt32 nWidth(cairo_image_surface_get_width(pTarget)); + const sal_uInt32 nHeight(cairo_image_surface_get_height(pTarget)); + const tools::Long nMiX((nWidth / 2) + 1); + const tools::Long nMiY((nHeight / 2) + 1); + + cairo_save(mpRT); + cairo_identity_matrix(mpRT); + const cairo_antialias_t eOldAAMode(cairo_get_antialias(mpRT)); + cairo_set_antialias(mpRT, CAIRO_ANTIALIAS_NONE); + + for (auto const& pos : rPositions) + { + const basegfx::B2DPoint aDiscretePos(getViewInformation2D().getObjectToViewTransformation() + * pos); + const double fX(ceil(aDiscretePos.getX())); + const double fY(ceil(aDiscretePos.getY())); + + cairo_set_source_surface(mpRT, pTarget, fX - nMiX, fY - nMiY); + cairo_paint(mpRT); + } + + cairo_set_antialias(mpRT, eOldAAMode); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processBackgroundColorPrimitive2D( + const primitive2d::BackgroundColorPrimitive2D& rBackgroundColorCandidate) +{ + // check for allowed range [0.0 .. 1.0[ + if (rBackgroundColorCandidate.getTransparency() < 0.0 + || rBackgroundColorCandidate.getTransparency() >= 1.0) + return; + + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::NoFill) + // NoFill wanted, done + return; + + if (!getViewInformation2D().getViewport().isEmpty()) + { + // we have a Viewport set with limitations, render as needed/defined + // by BackgroundColorPrimitive2D::create2DDecomposition. Alternatively, + // just use recursion/decompose in this case + process(rBackgroundColorCandidate); + return; + } + + // no Viewport set, render surface completely + cairo_save(mpRT); + basegfx::BColor aFillColor(getFillColor(rBackgroundColorCandidate.getBColor())); + aFillColor = maBColorModifierStack.getModifiedColor(aFillColor); + cairo_set_source_rgba(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue(), + 1.0 - rBackgroundColorCandidate.getTransparency()); + // to also copy alpha part of color, see cairo docu. Will be reset by restore below + cairo_set_operator(mpRT, CAIRO_OPERATOR_SOURCE); + cairo_paint(mpRT); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processPolygonStrokePrimitive2D( + const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate) +{ + const basegfx::B2DPolygon& rPolygon(rPolygonStrokeCandidate.getB2DPolygon()); + const attribute::LineAttribute& rLineAttribute(rPolygonStrokeCandidate.getLineAttribute()); + + if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0) + { + // no geometry, done + return; + } + + // get some values early that might be used for decisions + const bool bHairline(0.0 == rLineAttribute.getWidth()); + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + const double fDiscreteLineWidth( + bHairline + ? 1.0 + : (rObjectToView * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)).getLength()); + + // Here for every combination which the system-specific implementation is not + // capable of visualizing, use the (for decomposable Primitives always possible) + // fallback to the decomposition. + if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && fDiscreteLineWidth > 1.5) + { + // basegfx::B2DLineJoin::NONE is special for our office, no other GraphicSystem + // knows that (so far), so fallback to decomposition. This is only needed if + // LineJoin will be used, so also check for discrete LineWidth before falling back + process(rPolygonStrokeCandidate); + return; + } + + // This is a method every system-specific implementation of a decomposable Primitive + // can use to allow simple optical control of paint implementation: + // Create a copy, e.g. change color to 'red' as here and paint before the system + // paints it using the decomposition. That way you can - if active - directly + // optically compare if the system-specific solution is geometrically identical to + // the decomposition (which defines our interpretation that we need to visualize). + // Look below in the impl for bRenderDecomposeForCompareInRed to see that in that case + // we create a half-transparent paint to better support visual control + static bool bRenderDecomposeForCompareInRed(false); + + if (bRenderDecomposeForCompareInRed) + { + const attribute::LineAttribute aRed( + basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), rLineAttribute.getLineJoin(), + rLineAttribute.getLineCap(), rLineAttribute.getMiterMinimumAngle()); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xCopy( + new primitive2d::PolygonStrokePrimitive2D( + rPolygonStrokeCandidate.getB2DPolygon(), aRed, + rPolygonStrokeCandidate.getStrokeAttribute())); + process(*xCopy); + } + + cairo_save(mpRT); + + // setup line attributes + cairo_line_join_t eCairoLineJoin = CAIRO_LINE_JOIN_MITER; + switch (rLineAttribute.getLineJoin()) + { + case basegfx::B2DLineJoin::Bevel: + eCairoLineJoin = CAIRO_LINE_JOIN_BEVEL; + break; + case basegfx::B2DLineJoin::Round: + eCairoLineJoin = CAIRO_LINE_JOIN_ROUND; + break; + case basegfx::B2DLineJoin::NONE: + case basegfx::B2DLineJoin::Miter: + eCairoLineJoin = CAIRO_LINE_JOIN_MITER; + break; + } + cairo_set_line_join(mpRT, eCairoLineJoin); + + // convert miter minimum angle to miter limit + double fMiterLimit + = 1.0 / sin(std::max(rLineAttribute.getMiterMinimumAngle(), 0.01 * M_PI) / 2.0); + cairo_set_miter_limit(mpRT, fMiterLimit); + + // setup cap attribute + cairo_line_cap_t eCairoLineCap(CAIRO_LINE_CAP_BUTT); + switch (rLineAttribute.getLineCap()) + { + default: // css::drawing::LineCap_BUTT: + { + eCairoLineCap = CAIRO_LINE_CAP_BUTT; + break; + } + case css::drawing::LineCap_ROUND: + { + eCairoLineCap = CAIRO_LINE_CAP_ROUND; + break; + } + case css::drawing::LineCap_SQUARE: + { + eCairoLineCap = CAIRO_LINE_CAP_SQUARE; + break; + } + } + cairo_set_line_cap(mpRT, eCairoLineCap); + + // determine & set color + basegfx::BColor aLineColor(getLineColor(rLineAttribute.getColor())); + aLineColor = maBColorModifierStack.getModifiedColor(aLineColor); + if (bRenderDecomposeForCompareInRed) + aLineColor.setRed(0.5); + cairo_set_source_rgb(mpRT, aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue()); + + // check stroke + const attribute::StrokeAttribute& rStrokeAttribute( + rPolygonStrokeCandidate.getStrokeAttribute()); + const bool bDashUsed(!rStrokeAttribute.isDefault() + && !rStrokeAttribute.getDotDashArray().empty() + && 0.0 < rStrokeAttribute.getFullDotDashLen()); + if (isCairoCoordinateLimitWorkaroundActive()) + { + // need to fallback to paint in view coordinates, unfortunately + // need to transform self (cairo will do it wrong in this coordinate + // space), so no need to try to buffer + cairo_new_path(mpRT); + basegfx::B2DPolygon aAdaptedPolygon(rPolygon); + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + aAdaptedPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(fAAOffset, fAAOffset) + * getViewInformation2D().getObjectToViewTransformation()); + cairo_identity_matrix(mpRT); + addB2DPolygonToPathGeometry(mpRT, aAdaptedPolygon); + + // process/set LineWidth + const double fObjectLineWidth(bHairline + ? 1.0 + : (getViewInformation2D().getObjectToViewTransformation() + * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)) + .getLength()); + cairo_set_line_width(mpRT, fObjectLineWidth); + + if (bDashUsed) + { + std::vector<double> aStroke(rStrokeAttribute.getDotDashArray()); + for (auto& rCandidate : aStroke) + rCandidate = (getViewInformation2D().getObjectToViewTransformation() + * basegfx::B2DVector(rCandidate, 0.0)) + .getLength(); + cairo_set_dash(mpRT, aStroke.data(), aStroke.size(), 0.0); + } + + cairo_stroke(mpRT); + } + else + { + // set linear transformation + cairo_matrix_t aMatrix; + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e() + fAAOffset, + rObjectToView.f() + fAAOffset); + cairo_set_matrix(mpRT, &aMatrix); + + // create path geometry and put mask as path + cairo_new_path(mpRT); + getOrCreatePathGeometry(mpRT, rPolygon, getViewInformation2D(), + bHairline && getViewInformation2D().getUseAntiAliasing()); + + // process/set LineWidth + const double fObjectLineWidth( + bHairline ? (getViewInformation2D().getInverseObjectToViewTransformation() + * basegfx::B2DVector(1.0, 0.0)) + .getLength() + : rLineAttribute.getWidth()); + cairo_set_line_width(mpRT, fObjectLineWidth); + + if (bDashUsed) + { + const std::vector<double>& rStroke = rStrokeAttribute.getDotDashArray(); + cairo_set_dash(mpRT, rStroke.data(), rStroke.size(), 0.0); + } + + // render + cairo_stroke(mpRT); + } + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processLineRectanglePrimitive2D( + const primitive2d::LineRectanglePrimitive2D& rLineRectanglePrimitive2D) +{ + if (rLineRectanglePrimitive2D.getB2DRange().isEmpty()) + { + // no geometry, done + return; + } + + cairo_save(mpRT); + + // work in view coordinates + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + basegfx::B2DRange aRange(rLineRectanglePrimitive2D.getB2DRange()); + aRange.transform(getViewInformation2D().getObjectToViewTransformation()); + cairo_identity_matrix(mpRT); + + basegfx::BColor aHairlineColor(getLineColor(rLineRectanglePrimitive2D.getBColor())); + aHairlineColor = maBColorModifierStack.getModifiedColor(aHairlineColor); + cairo_set_source_rgb(mpRT, aHairlineColor.getRed(), aHairlineColor.getGreen(), + aHairlineColor.getBlue()); + + const double fDiscreteLineWidth((getViewInformation2D().getInverseObjectToViewTransformation() + * basegfx::B2DVector(1.0, 0.0)) + .getLength()); + cairo_set_line_width(mpRT, fDiscreteLineWidth); + + cairo_rectangle(mpRT, aRange.getMinX() + fAAOffset, aRange.getMinY() + fAAOffset, + aRange.getWidth(), aRange.getHeight()); + cairo_stroke(mpRT); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFilledRectanglePrimitive2D( + const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D) +{ + if (rFilledRectanglePrimitive2D.getB2DRange().isEmpty()) + { + // no geometry, done + return; + } + + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::NoFill) + // NoFill wanted, done + return; + + cairo_save(mpRT); + + // work in view coordinates + basegfx::B2DRange aRange(rFilledRectanglePrimitive2D.getB2DRange()); + aRange.transform(getViewInformation2D().getObjectToViewTransformation()); + cairo_identity_matrix(mpRT); + + basegfx::BColor aFillColor(getFillColor(rFilledRectanglePrimitive2D.getBColor())); + aFillColor = maBColorModifierStack.getModifiedColor(aFillColor); + cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue()); + + cairo_rectangle(mpRT, aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), + aRange.getHeight()); + cairo_fill(mpRT); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processSingleLinePrimitive2D( + const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D) +{ + cairo_save(mpRT); + + basegfx::BColor aLineColor(getLineColor(rSingleLinePrimitive2D.getBColor())); + aLineColor = maBColorModifierStack.getModifiedColor(aLineColor); + cairo_set_source_rgb(mpRT, aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue()); + + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + const basegfx::B2DPoint aStart(rObjectToView * rSingleLinePrimitive2D.getStart()); + const basegfx::B2DPoint aEnd(rObjectToView * rSingleLinePrimitive2D.getEnd()); + cairo_identity_matrix(mpRT); + + cairo_set_line_width(mpRT, 1.0f); + + cairo_move_to(mpRT, aStart.getX() + fAAOffset, aStart.getY() + fAAOffset); + cairo_line_to(mpRT, aEnd.getX() + fAAOffset, aEnd.getY() + fAAOffset); + cairo_stroke(mpRT); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGraphicPrimitive2D( + const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D) +{ + if (rFillGraphicPrimitive2D.getTransparency() < 0.0 + || rFillGraphicPrimitive2D.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + Bitmap aPreparedBitmap; + basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange()); + constexpr double fBigDiscreteArea(300.0 * 300.0); + + // use tooling to do various checks and prepare tiled rendering, see + // description of method, parameters and return value there + if (!prepareBitmapForDirectRender(rFillGraphicPrimitive2D, getViewInformation2D(), + aPreparedBitmap, aFillUnitRange, fBigDiscreteArea)) + { + // no output needed, done + return; + } + + if (aPreparedBitmap.IsEmpty()) + { + // output needed and Bitmap data empty, so no bitmap data based + // tiled rendering is suggested. Use fallback for paint + // and decomposition + process(rFillGraphicPrimitive2D); + return; + } + + // work with dimensions in discrete target pixels to use evtl. MipMap pre-scale + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation() + * rFillGraphicPrimitive2D.getTransformation()); + const tools::Long nDestWidth( + (aLocalTransform * basegfx::B2DVector(aFillUnitRange.getWidth(), 0.0)).getLength()); + const tools::Long nDestHeight( + (aLocalTransform * basegfx::B2DVector(0.0, aFillUnitRange.getHeight())).getLength()); + + // tdf#167831 check for output size, may have zero discrete dimension in X and/or Y + if (0 == nDestWidth || 0 == nDestHeight) + { + // it has and is thus invisible + return; + } + + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); + basegfx::BColor aReplacementColor(0, 0, 0); + bool bTemporaryGrayColorModifier(false); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + if (aDrawModeFlags & BITMAP) + { + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + // aReplacementColor already set + aPreparedBitmap.SetEmpty(); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + aReplacementColor = basegfx::BColor(1, 1, 1); + aPreparedBitmap.SetEmpty(); + } + else // DrawModeFlags::GrayBitmap + { + bTemporaryGrayColorModifier = true; + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + } + + if (!aPreparedBitmap.IsEmpty() && maBColorModifierStack.count()) + { + // apply ColorModifier to Bitmap data + aPreparedBitmap = aPreparedBitmap.Modify(maBColorModifierStack); + + if (aPreparedBitmap.IsEmpty()) + { + // color gets completely replaced, get it + aReplacementColor = maBColorModifierStack.getModifiedColor(basegfx::BColor()); + } + + if (bTemporaryGrayColorModifier) + // cleanup temporary BColorModifier + maBColorModifierStack.pop(); + } + + // if PreparedBitmap is empty, draw geometry in single color using + // prepared ReplacementColor + if (aPreparedBitmap.IsEmpty()) + { + // use unit geometry as fallback object geometry. Do *not* + // transform, the below used method will use the already + // correctly initialized local ViewInformation + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + + // what we still need to apply is the object transform from the + // local primitive, that is not part of DisplayInfo yet + aPolygon.transform(rFillGraphicPrimitive2D.getTransformation()); + + // draw directly, done + paintPolyPolygonRGBA(basegfx::B2DPolyPolygon(aPolygon), aReplacementColor, + rFillGraphicPrimitive2D.getTransparency()); + return; + } + + // access or create cairo bitmap data + std::shared_ptr<CairoSurfaceHelper> aCairoSurfaceHelper( + getOrCreateCairoSurfaceHelper(aPreparedBitmap)); + if (!aCairoSurfaceHelper) + { + SAL_WARN("drawinglayer", "SDPRCairo: No SurfaceHelper from Bitmap (!)"); + return; + } + + cairo_surface_t* pTarget(aCairoSurfaceHelper->getCairoSurface(nDestWidth, nDestHeight)); + if (nullptr == pTarget) + { + SAL_WARN("drawinglayer", "SDPRCairo: No CairoSurface from Bitmap SurfaceHelper (!)"); + return; + } + + cairo_save(mpRT); + + // set linear transformation - no fAAOffset for bitmap data + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), + aLocalTransform.d(), aLocalTransform.e(), aLocalTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + + const sal_uInt32 nWidth(cairo_image_surface_get_width(pTarget)); + const sal_uInt32 nHeight(cairo_image_surface_get_height(pTarget)); + + cairo_set_source_surface(mpRT, pTarget, 0, 0); + + // get the pattern created by cairo_set_source_surface and + // it's transformation + cairo_pattern_t* sourcepattern = cairo_get_source(mpRT); + cairo_pattern_get_matrix(sourcepattern, &aMatrix); + + // clip for RGBA (see other places) + if (CAIRO_FORMAT_ARGB32 == cairo_image_surface_get_format(pTarget)) + { + cairo_rectangle(mpRT, 0, 0, 1, 1); + cairo_clip(mpRT); + } + + // create transformation for source pattern (inverse, see + // cairo docu: uses user space to pattern space transformation) + cairo_matrix_init_scale(&aMatrix, nWidth / aFillUnitRange.getWidth(), + nHeight / aFillUnitRange.getHeight()); + cairo_matrix_translate(&aMatrix, -aFillUnitRange.getMinX(), -aFillUnitRange.getMinY()); + + // set source pattern transform & activate pattern repeat + cairo_pattern_set_matrix(sourcepattern, &aMatrix); + cairo_pattern_set_extend(sourcepattern, CAIRO_EXTEND_REPEAT); + + // CAIRO_FILTER_GOOD seems to be the default anyways, but set it + // to be on the safe side + cairo_pattern_set_filter(sourcepattern, CAIRO_FILTER_GOOD); + + // paint + if (rFillGraphicPrimitive2D.hasTransparency()) + cairo_paint_with_alpha(mpRT, 1.0 - rFillGraphicPrimitive2D.getTransparency()); + else + cairo_paint(mpRT); + + static bool bRenderTransformationBounds(false); + if (bRenderTransformationBounds) + { + cairo_set_source_rgba(mpRT, 0, 1, 0, 0.8); + impl_cairo_set_hairline(mpRT, getViewInformation2D(), + isCairoCoordinateLimitWorkaroundActive()); + // full object + cairo_rectangle(mpRT, 0, 0, 1, 1); + // outline of pattern root image + cairo_rectangle(mpRT, aFillUnitRange.getMinX(), aFillUnitRange.getMinY(), + aFillUnitRange.getWidth(), aFillUnitRange.getHeight()); + cairo_stroke(mpRT); + } + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D_drawOutputRange( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + // prepare outer color + basegfx::BColor aOuterColor(getGradientColor(rFillGradientPrimitive2D.getOuterColor())); + aOuterColor = maBColorModifierStack.getModifiedColor(aOuterColor); + + cairo_save(mpRT); + + // fill simple rect with outer color + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + const attribute::FillGradientAttribute& rAlphaGradient( + rFillGradientPrimitive2D.getAlphaGradient()); + double fLuminance(0.0); + + if (!rAlphaGradient.getColorStops().empty()) + { + if (css::awt::GradientStyle_AXIAL == rAlphaGradient.getStyle()) + fLuminance = rAlphaGradient.getColorStops().back().getStopColor().luminance(); + else + fLuminance = rAlphaGradient.getColorStops().front().getStopColor().luminance(); + } + + cairo_set_source_rgba(mpRT, aOuterColor.getRed(), aOuterColor.getGreen(), + aOuterColor.getBlue(), 1.0 - fLuminance); + } + else + { + cairo_set_source_rgb(mpRT, aOuterColor.getRed(), aOuterColor.getGreen(), + aOuterColor.getBlue()); + } + + const basegfx::B2DHomMatrix aTrans(getViewInformation2D().getObjectToViewTransformation()); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), aTrans.d(), aTrans.e(), + aTrans.f()); + cairo_set_matrix(mpRT, &aMatrix); + + const basegfx::B2DRange& rRange(rFillGradientPrimitive2D.getOutputRange()); + cairo_rectangle(mpRT, rRange.getMinX(), rRange.getMinY(), rRange.getWidth(), + rRange.getHeight()); + cairo_fill(mpRT); + + cairo_restore(mpRT); +} + +bool CairoPixelProcessor2D::processFillGradientPrimitive2D_isCompletelyBordered( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + const double fBorder(rFillGradient.getBorder()); + + // check if completely 'bordered out'. This can be the case for all + // types of gradients + if (basegfx::fTools::less(fBorder, 1.0) && fBorder >= 0.0) + { + // no, we have visible content besides border + return false; + } + + // draw all-covering polygon using getOuterColor and getOutputRange + processFillGradientPrimitive2D_drawOutputRange(rFillGradientPrimitive2D); + return true; +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D_linear_axial( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + assert(!rFillGradientPrimitive2D.hasAlphaGradient() + || rFillGradient.sameDefinitionThanAlpha(rFillGradientPrimitive2D.getAlphaGradient())); + assert( + (css::awt::GradientStyle_LINEAR == rFillGradientPrimitive2D.getFillGradient().getStyle() + || css::awt::GradientStyle_AXIAL == rFillGradientPrimitive2D.getFillGradient().getStyle()) + && "SDPRCairo: Helper allows only SPECIFIED types (!)"); + cairo_save(mpRT); + + // need to do 'antique' stuff adaptions for rotate/transitionStart in object coordinates + // (DefinitionRange) to have the right 'bending' on rotation + basegfx::B2DRange aAdaptedRange(rFillGradientPrimitive2D.getDefinitionRange()); + const double fAngle(basegfx::normalizeToRange((2 * M_PI) - rFillGradient.getAngle(), 2 * M_PI)); + const bool bAngle(!basegfx::fTools::equalZero(fAngle)); + const basegfx::B2DPoint aCenter(aAdaptedRange.getCenter()); + + // pack rotation and offset into a transformation covering that part + basegfx::B2DHomMatrix aRotation(basegfx::utils::createRotateAroundPoint(aCenter, fAngle)); + + // create local transform to work in object coordinates based on OutputRange, + // combine with rotation - that way we can then just draw into AdaptedRange + basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() + * aRotation); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), + aLocalTransform.d(), aLocalTransform.e(), aLocalTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + + if (bAngle) + { + // expand Range by rotating + aAdaptedRange.transform(aRotation); + } + + // create linear pattern in unit coordinates in y-direction + cairo_pattern_t* pPattern( + cairo_pattern_create_linear(aAdaptedRange.getCenterX(), aAdaptedRange.getMinY(), + aAdaptedRange.getCenterX(), aAdaptedRange.getMaxY())); + + // get color stops (make copy, might have to be changed) + basegfx::BColorStops aBColorStops(rFillGradient.getColorStops()); + basegfx::BColorStops aBColorStopsAlpha; + const bool bHasAlpha(rFillGradientPrimitive2D.hasAlphaGradient()); + if (bHasAlpha) + aBColorStopsAlpha = rFillGradientPrimitive2D.getAlphaGradient().getColorStops(); + const bool bAxial(css::awt::GradientStyle_AXIAL == rFillGradient.getStyle()); + + // get and apply border - create soace at start in gradient + const double fBorder(std::max(std::min(rFillGradient.getBorder(), 1.0), 0.0)); + if (!basegfx::fTools::equalZero(fBorder)) + { + if (bAxial) + { + aBColorStops.reverseColorStops(); + if (bHasAlpha) + aBColorStopsAlpha.reverseColorStops(); + } + + aBColorStops.createSpaceAtStart(fBorder); + if (bHasAlpha) + aBColorStopsAlpha.createSpaceAtStart(fBorder); + + if (bAxial) + { + aBColorStops.reverseColorStops(); + if (bHasAlpha) + aBColorStopsAlpha.reverseColorStops(); + } + } + + if (bAxial) + { + // expand with mirrored ColorStops to create axial + aBColorStops.doApplyAxial(); + if (bHasAlpha) + aBColorStopsAlpha.doApplyAxial(); + } + + // Apply steps if used to 'emulate' LO's 'discrete step' feature + if (rFillGradient.getSteps()) + { + aBColorStops.doApplySteps(rFillGradient.getSteps()); + if (bHasAlpha) + aBColorStopsAlpha.doApplySteps(rFillGradient.getSteps()); + } + + // add color stops + for (size_t a(0); a < aBColorStops.size(); a++) + { + const basegfx::BColorStop& rStop(aBColorStops[a]); + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rStop.getStopColor())); + + if (bHasAlpha) + { + const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor()); + cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), + 1.0 - aAlpha.luminance()); + } + else + { + if (rFillGradientPrimitive2D.hasTransparency()) + { + cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), + 1.0 - rFillGradientPrimitive2D.getTransparency()); + } + else + { + cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue()); + } + } + } + + // draw OutRange + basegfx::B2DRange aOutRange(rFillGradientPrimitive2D.getOutputRange()); + if (bAngle) + { + // expand backwards to cover all area needed for OutputRange + aRotation.invert(); + aOutRange.transform(aRotation); + } + cairo_rectangle(mpRT, aOutRange.getMinX(), aOutRange.getMinY(), aOutRange.getWidth(), + aOutRange.getHeight()); + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + + // cleanup + cairo_pattern_destroy(pPattern); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D_square_rect( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + if (rFillGradientPrimitive2D.hasAlphaGradient() || rFillGradientPrimitive2D.hasTransparency()) + { + // Do not use direct alpha for this: It paints using four trapez that + // do not add up at edges due to being painted AntiAliased; that means + // common pixels do not add up, but blend by transparency, so leaving + // visual traces -> process recursively + process(rFillGradientPrimitive2D); + return; + } + + assert( + (css::awt::GradientStyle_SQUARE == rFillGradientPrimitive2D.getFillGradient().getStyle() + || css::awt::GradientStyle_RECT == rFillGradientPrimitive2D.getFillGradient().getStyle()) + && "SDPRCairo: Helper allows only SPECIFIED types (!)"); + cairo_save(mpRT); + + // draw all-covering polygon using getOuterColor and getOutputRange, + // the partial paints below will not fill areas outside automatically + // as happens in the other gradient paints + processFillGradientPrimitive2D_drawOutputRange(rFillGradientPrimitive2D); + + // get DefinitionRange and adapt if needed + basegfx::B2DRange aAdaptedRange(rFillGradientPrimitive2D.getDefinitionRange()); + const bool bSquare(css::awt::GradientStyle_SQUARE + == rFillGradientPrimitive2D.getFillGradient().getStyle()); + const basegfx::B2DPoint aCenter(aAdaptedRange.getCenter()); + bool bLandscape(false); + double fSmallRadius(1.0); + + // get rotation and offset values + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + const double fAngle(basegfx::normalizeToRange((2 * M_PI) - rFillGradient.getAngle(), 2 * M_PI)); + const bool bAngle(!basegfx::fTools::equalZero(fAngle)); + const double fOffxsetX(std::max(std::min(rFillGradient.getOffsetX(), 1.0), 0.0)); + const double fOffxsetY(std::max(std::min(rFillGradient.getOffsetY(), 1.0), 0.0)); + + if (bSquare) + { + // expand to make width == height + const basegfx::B2DRange& rDefRange(rFillGradientPrimitive2D.getDefinitionRange()); + + if (rDefRange.getWidth() > rDefRange.getHeight()) + { + // landscape -> square + const double fRadius(0.5 * rDefRange.getWidth()); + aAdaptedRange.expand(basegfx::B2DPoint(rDefRange.getMinX(), aCenter.getY() - fRadius)); + aAdaptedRange.expand(basegfx::B2DPoint(rDefRange.getMaxX(), aCenter.getY() + fRadius)); + } + else + { + // portrait -> square + const double fRadius(0.5 * rDefRange.getHeight()); + aAdaptedRange.expand(basegfx::B2DPoint(aCenter.getX() - fRadius, rDefRange.getMinY())); + aAdaptedRange.expand(basegfx::B2DPoint(aCenter.getX() + fRadius, rDefRange.getMaxY())); + } + + bLandscape = true; + fSmallRadius = 0.5 * aAdaptedRange.getWidth(); + } + else + { + if (bAngle) + { + // expand range using applied rotation + aAdaptedRange.transform(basegfx::utils::createRotateAroundPoint(aCenter, fAngle)); + } + + // set local params as needed for non-square + bLandscape = aAdaptedRange.getWidth() > aAdaptedRange.getHeight(); + fSmallRadius = 0.5 * (bLandscape ? aAdaptedRange.getHeight() : aAdaptedRange.getWidth()); + } + + // pack rotation and offset into a combined transformation that covers that parts + basegfx::B2DHomMatrix aRotAndTranslate; + aRotAndTranslate.translate(-aCenter.getX(), -aCenter.getY()); + if (bAngle) + aRotAndTranslate.rotate(fAngle); + aRotAndTranslate.translate(aAdaptedRange.getMinX() + (fOffxsetX * aAdaptedRange.getWidth()), + aAdaptedRange.getMinY() + (fOffxsetY * aAdaptedRange.getHeight())); + + // create local transform to work in object coordinates based on OutputRange, + // combine with rotation and offset - that way we can then just draw into + // AdaptedRange + basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() + * aRotAndTranslate); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), + aLocalTransform.d(), aLocalTransform.e(), aLocalTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // get color stops (make copy, might have to be changed) + basegfx::BColorStops aBColorStops(rFillGradient.getColorStops()); + + // apply BColorModifierStack early - the BColorStops are used multiple + // times below, so do this only once + if (0 != maBColorModifierStack.count()) + { + aBColorStops.tryToApplyBColorModifierStack(maBColorModifierStack); + } + + // get and apply border - create soace at start in gradient + const double fBorder(std::max(std::min(rFillGradient.getBorder(), 1.0), 0.0)); + if (!basegfx::fTools::equalZero(fBorder)) + { + aBColorStops.createSpaceAtStart(fBorder); + } + + // Apply steps if used to 'emulate' LO's 'discrete step' feature + if (rFillGradient.getSteps()) + { + aBColorStops.doApplySteps(rFillGradient.getSteps()); + } + + // get half single pixel size to fill touching 'gaps' + // NOTE: I formally used cairo_device_to_user_distance, but that + // can indeed create negative sizes if the transformation e.g. + // contains rotation(s). could use fabs(), but just rely on + // linear algebra and use the (always positive) length of a vector + const double fHalfPx((getViewInformation2D().getInverseObjectToViewTransformation() + * basegfx::B2DVector(1.0, 0.0)) + .getLength()); + + // draw top part trapez/triangle + { + cairo_move_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMinY()); + cairo_line_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMinY()); + cairo_line_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMinY() + fHalfPx); + if (!bSquare && bLandscape) + { + cairo_line_to(mpRT, aAdaptedRange.getMaxX() - fSmallRadius, aCenter.getY() + fHalfPx); + cairo_line_to(mpRT, aAdaptedRange.getMinX() + fSmallRadius, aCenter.getY() + fHalfPx); + } + else + { + cairo_line_to(mpRT, aCenter.getX(), aAdaptedRange.getMinY() + fSmallRadius + fHalfPx); + } + cairo_line_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMinY() + fHalfPx); + cairo_close_path(mpRT); + + // create linear pattern in needed coordinates directly + // NOTE: I *tried* to create in unit coordinates and adapt modifying and re-using + // cairo_pattern_set_matrix - that *seems* to work but sometimes runs into + // numerical problems -> probably cairo implementation. So stay safe and do + // it the easy way, for the cost of re-creating gradient definitions (still cheap) + cairo_pattern_t* pPattern(cairo_pattern_create_linear( + aCenter.getX(), aAdaptedRange.getMinY(), aCenter.getX(), + aAdaptedRange.getMinY() + + (bLandscape ? aAdaptedRange.getHeight() * 0.5 : fSmallRadius))); + for (const auto& aStop : aBColorStops) + { + const basegfx::BColor& rColor(aStop.getStopColor()); + cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), rColor.getRed(), + rColor.getGreen(), rColor.getBlue()); + } + + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + cairo_pattern_destroy(pPattern); + } + + { + // draw right part trapez/triangle + cairo_move_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMinY()); + cairo_line_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMaxY()); + if (bSquare || bLandscape) + { + cairo_line_to(mpRT, aAdaptedRange.getMaxX() - fSmallRadius - fHalfPx, aCenter.getY()); + } + else + { + cairo_line_to(mpRT, aCenter.getX() - fHalfPx, aAdaptedRange.getMaxY() - fSmallRadius); + cairo_line_to(mpRT, aCenter.getX() - fHalfPx, aAdaptedRange.getMinY() + fSmallRadius); + } + cairo_close_path(mpRT); + + // create linear pattern in needed coordinates directly + cairo_pattern_t* pPattern(cairo_pattern_create_linear( + aAdaptedRange.getMaxX(), aCenter.getY(), + aAdaptedRange.getMaxX() - (bLandscape ? fSmallRadius : aAdaptedRange.getWidth() * 0.5), + aCenter.getY())); + for (const auto& aStop : aBColorStops) + { + const basegfx::BColor& rColor(aStop.getStopColor()); + cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), rColor.getRed(), + rColor.getGreen(), rColor.getBlue()); + } + + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + cairo_pattern_destroy(pPattern); + } + + { + // draw bottom part trapez/triangle + cairo_move_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMaxY()); + cairo_line_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMaxY()); + cairo_line_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMaxY() - fHalfPx); + if (!bSquare && bLandscape) + { + cairo_line_to(mpRT, aAdaptedRange.getMinX() + fSmallRadius, aCenter.getY() - fHalfPx); + cairo_line_to(mpRT, aAdaptedRange.getMaxX() - fSmallRadius, aCenter.getY() - fHalfPx); + } + else + { + cairo_line_to(mpRT, aCenter.getX(), aAdaptedRange.getMaxY() - fSmallRadius - fHalfPx); + } + cairo_line_to(mpRT, aAdaptedRange.getMaxX(), aAdaptedRange.getMaxY() - fHalfPx); + cairo_close_path(mpRT); + + // create linear pattern in needed coordinates directly + cairo_pattern_t* pPattern(cairo_pattern_create_linear( + aCenter.getX(), aAdaptedRange.getMaxY(), aCenter.getX(), + aAdaptedRange.getMaxY() + - (bLandscape ? aAdaptedRange.getHeight() * 0.5 : fSmallRadius))); + for (const auto& aStop : aBColorStops) + { + const basegfx::BColor& rColor(aStop.getStopColor()); + cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), rColor.getRed(), + rColor.getGreen(), rColor.getBlue()); + } + + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + cairo_pattern_destroy(pPattern); + } + + { + // draw left part trapez/triangle + cairo_move_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMaxY()); + cairo_line_to(mpRT, aAdaptedRange.getMinX(), aAdaptedRange.getMinY()); + if (bSquare || bLandscape) + { + cairo_line_to(mpRT, aAdaptedRange.getMinX() + fSmallRadius + fHalfPx, aCenter.getY()); + } + else + { + cairo_line_to(mpRT, aCenter.getX() + fHalfPx, aAdaptedRange.getMinY() + fSmallRadius); + cairo_line_to(mpRT, aCenter.getX() + fHalfPx, aAdaptedRange.getMaxY() - fSmallRadius); + } + cairo_close_path(mpRT); + + // create linear pattern in needed coordinates directly + cairo_pattern_t* pPattern(cairo_pattern_create_linear( + aAdaptedRange.getMinX(), aCenter.getY(), + aAdaptedRange.getMinX() + (bLandscape ? fSmallRadius : aAdaptedRange.getWidth() * 0.5), + aCenter.getY())); + for (const auto& aStop : aBColorStops) + { + const basegfx::BColor& rColor(aStop.getStopColor()); + cairo_pattern_add_color_stop_rgb(pPattern, aStop.getStopOffset(), rColor.getRed(), + rColor.getGreen(), rColor.getBlue()); + } + + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + cairo_pattern_destroy(pPattern); + } + + // cleanup + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D_radial_elliptical( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + assert(!rFillGradientPrimitive2D.hasAlphaGradient() + || rFillGradient.sameDefinitionThanAlpha(rFillGradientPrimitive2D.getAlphaGradient())); + assert((css::awt::GradientStyle_RADIAL == rFillGradientPrimitive2D.getFillGradient().getStyle() + || css::awt::GradientStyle_ELLIPTICAL + == rFillGradientPrimitive2D.getFillGradient().getStyle()) + && "SDPRCairo: Helper allows only SPECIFIED types (!)"); + cairo_save(mpRT); + + // need to do 'antique' stuff adaptions for rotate/transitionStart in object coordinates + // (DefinitionRange) to have the right 'bending' on rotation + const basegfx::B2DRange rDefRange(rFillGradientPrimitive2D.getDefinitionRange()); + const basegfx::B2DPoint aCenter(rDefRange.getCenter()); + double fRadius(1.0); + double fRatioElliptical(1.0); + const bool bRadial(css::awt::GradientStyle_RADIAL == rFillGradient.getStyle()); + + // use what is done in initEllipticalGradientInfo method to get as close as + // possible to former stuff, expand AdaptedRange as needed + if (bRadial) + { + const double fHalfOriginalDiag(std::hypot(rDefRange.getWidth(), rDefRange.getHeight()) + * 0.5); + fRadius = fHalfOriginalDiag; + } + else + { + double fTargetSizeX(M_SQRT2 * rDefRange.getWidth()); + double fTargetSizeY(M_SQRT2 * rDefRange.getHeight()); + fRatioElliptical = fTargetSizeX / fTargetSizeY; + fRadius = std::max(fTargetSizeX, fTargetSizeY) * 0.5; + } + + // get rotation and offset values + const double fAngle(basegfx::normalizeToRange((2 * M_PI) - rFillGradient.getAngle(), 2 * M_PI)); + const bool bAngle(!basegfx::fTools::equalZero(fAngle)); + const double fOffxsetX(std::max(std::min(rFillGradient.getOffsetX(), 1.0), 0.0)); + const double fOffxsetY(std::max(std::min(rFillGradient.getOffsetY(), 1.0), 0.0)); + + // pack rotation and offset into a combined transformation covering that parts + basegfx::B2DHomMatrix aRotAndTranslate; + aRotAndTranslate.translate(-aCenter.getX(), -aCenter.getY()); + if (bAngle) + aRotAndTranslate.rotate(fAngle); + aRotAndTranslate.translate(rDefRange.getMinX() + (fOffxsetX * rDefRange.getWidth()), + rDefRange.getMinY() + (fOffxsetY * rDefRange.getHeight())); + + // create local transform to work in object coordinates based on OutputRange, + // combine with rotation and offset - that way we can then just draw into + // AdaptedRange + basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() + * aRotAndTranslate); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), + aLocalTransform.d(), aLocalTransform.e(), aLocalTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // create linear pattern in unit coordinates in y-direction + cairo_pattern_t* pPattern(cairo_pattern_create_radial(aCenter.getX(), aCenter.getY(), fRadius, + aCenter.getX(), aCenter.getY(), 0.0)); + + // get color stops (make copy, might have to be changed) + basegfx::BColorStops aBColorStops(rFillGradient.getColorStops()); + basegfx::BColorStops aBColorStopsAlpha; + const bool bHasAlpha(rFillGradientPrimitive2D.hasAlphaGradient()); + if (bHasAlpha) + aBColorStopsAlpha = rFillGradientPrimitive2D.getAlphaGradient().getColorStops(); + + // get and apply border - create soace at start in gradient + const double fBorder(std::max(std::min(rFillGradient.getBorder(), 1.0), 0.0)); + if (!basegfx::fTools::equalZero(fBorder)) + { + aBColorStops.createSpaceAtStart(fBorder); + if (bHasAlpha) + aBColorStopsAlpha.createSpaceAtStart(fBorder); + } + + // Apply steps if used to 'emulate' LO's 'discrete step' feature + if (rFillGradient.getSteps()) + { + aBColorStops.doApplySteps(rFillGradient.getSteps()); + if (bHasAlpha) + aBColorStopsAlpha.doApplySteps(rFillGradient.getSteps()); + } + + // add color stops + for (size_t a(0); a < aBColorStops.size(); a++) + { + const basegfx::BColorStop& rStop(aBColorStops[a]); + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rStop.getStopColor())); + + if (bHasAlpha) + { + const basegfx::BColor aAlpha(aBColorStopsAlpha[a].getStopColor()); + cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), + 1.0 - aAlpha.luminance()); + } + else + { + if (rFillGradientPrimitive2D.hasTransparency()) + { + cairo_pattern_add_color_stop_rgba(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), + 1.0 - rFillGradientPrimitive2D.getTransparency()); + } + else + { + cairo_pattern_add_color_stop_rgb(pPattern, rStop.getStopOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue()); + } + } + } + + cairo_set_source(mpRT, pPattern); + + if (!bRadial) // css::awt::GradientStyle_ELLIPTICAL + { + // set cairo matrix at cairo_pattern_t to get needed ratio scale done. + // this is necessary since cairo_pattern_create_radial does *not* + // support ellipse resp. radial gradient with non-equidistant + // ratio directly + // this uses the transformation 'from user space to pattern space' as + // cairo docu states. That is the inverse of the intuitive thought + // model: describe from coordinates in texture, so use B2DHomMatrix + // and invert at the end to have better control about what has to happen + basegfx::B2DHomMatrix aTrans; + + // move center to origin to prepare scale/rotate + aTrans.translate(-aCenter.getX(), -aCenter.getY()); + + // get scale factor and apply as needed + if (fRatioElliptical > 1.0) + aTrans.scale(1.0, 1.0 / fRatioElliptical); + else + aTrans.scale(fRatioElliptical, 1.0); + + // move transformed stuff back to center + aTrans.translate(aCenter.getX(), aCenter.getY()); + + // invert and set at cairo_pattern_t + aTrans.invert(); + cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), aTrans.d(), aTrans.e(), + aTrans.f()); + cairo_pattern_set_matrix(pPattern, &aMatrix); + } + + // draw OutRange. Due to rot and translate being part of the + // set transform in cairo we need to back-transform (and expand + // as needed) the OutputRange to paint at the right place and + // get all OutputRange covered + basegfx::B2DRange aOutRange(rFillGradientPrimitive2D.getOutputRange()); + aRotAndTranslate.invert(); + aOutRange.transform(aRotAndTranslate); + cairo_rectangle(mpRT, aOutRange.getMinX(), aOutRange.getMinY(), aOutRange.getWidth(), + aOutRange.getHeight()); + cairo_fill(mpRT); + + // cleanup + cairo_pattern_destroy(pPattern); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D_fallback_decompose( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + if (rFillGradientPrimitive2D.hasAlphaGradient()) + { + // process recursively to eliminate alpha, cannot be used in decompose fallback + process(rFillGradientPrimitive2D); + return; + } + + // this helper draws the given gradient using the decompose fallback, + // maybe needed in some cases an can/will be handy + cairo_save(mpRT); + + // draw all-covering initial BG polygon 1st using getOuterColor and getOutputRange + processFillGradientPrimitive2D_drawOutputRange(rFillGradientPrimitive2D); + + // bet basic form in unit coordinates + CairoPathHelper aForm(rFillGradientPrimitive2D.getUnitPolygon()); + + // paint solid fill steps by providing callback as lambda + auto aCallback([this, &aForm](const basegfx::B2DHomMatrix& rMatrix, + const basegfx::BColor& rColor) { + const basegfx::B2DHomMatrix aTrans(getViewInformation2D().getObjectToViewTransformation() + * rMatrix); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), aTrans.d(), aTrans.e(), + aTrans.f()); + cairo_set_matrix(mpRT, &aMatrix); + + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(rColor)); + cairo_set_source_rgb(mpRT, aColor.getRed(), aColor.getGreen(), aColor.getBlue()); + + cairo_append_path(mpRT, aForm.getCairoPath()); + + cairo_fill(mpRT); + }); + + // call value generator to trigger callbacks + rFillGradientPrimitive2D.generateMatricesAndColors(aCallback); + + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::processFillGradientPrimitive2D( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + if (rFillGradientPrimitive2D.getDefinitionRange().isEmpty()) + { + // no definition area, done + return; + } + + if (rFillGradientPrimitive2D.getOutputRange().isEmpty()) + { + // no output area, done + return; + } + + const attribute::FillGradientAttribute& rFillGradient( + rFillGradientPrimitive2D.getFillGradient()); + + if (rFillGradient.isDefault()) + { + // no gradient definition, done + return; + } + + // check if completely 'bordered out' + if (processFillGradientPrimitive2D_isCompletelyBordered(rFillGradientPrimitive2D)) + { + // yes, done, was processed as single filled rectangle (using getOuterColor()) + return; + } + + constexpr DrawModeFlags SIMPLE_GRADIENT(DrawModeFlags::BlackGradient + | DrawModeFlags::WhiteGradient + | DrawModeFlags::SettingsGradient); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + if (aDrawModeFlags & SIMPLE_GRADIENT) + { + // use simple, single-color OutputRange draw + processFillGradientPrimitive2D_drawOutputRange(rFillGradientPrimitive2D); + return; + } + + const bool bTemporaryGrayColorModifier(aDrawModeFlags & DrawModeFlags::GrayGradient); + if (bTemporaryGrayColorModifier) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + + // evtl. prefer fallback: cairo does *not* render hard color transitions + // in gradients anti-aliased which is most visible in 'step'ed gradients, + // but may also happen in normal ones -> may need to be checked in + // basegfx::BColorStops (as tooling, like isSymmetrical() or similar). + // due to the nature of 'step'ing this also means a low number of + // filled polygons to be drawn (no 'smooth' parts to be replicated), + // so this is no runtime burner by definition. + // Making this configurable using static bool, may be moved to settings + // somewhere later. Do not forget to deactivate when working on 'step'ping + // stuff in the other helpers (!) + static bool bPreferAntiAliasedHardColorTransitions(true); + + if (bPreferAntiAliasedHardColorTransitions && rFillGradient.getSteps()) + { + processFillGradientPrimitive2D_fallback_decompose(rFillGradientPrimitive2D); + } + else + { + switch (rFillGradient.getStyle()) + { + case css::awt::GradientStyle_LINEAR: + case css::awt::GradientStyle_AXIAL: + { + // use specialized renderer for this cases - linear, axial + processFillGradientPrimitive2D_linear_axial(rFillGradientPrimitive2D); + break; + } + case css::awt::GradientStyle_RADIAL: + case css::awt::GradientStyle_ELLIPTICAL: + { + // use specialized renderer for this cases - radial, elliptical + + // NOTE for css::awt::GradientStyle_ELLIPTICAL: + // The first time ever I will accept slight deviations for the + // elliptical case here due to it's old chaotic move-two-pixels inside + // rendering method that cannot be patched into a lineartransformation + // and is hard/difficult to support in more modern systems. Differences + // are small and mostly would be visible *if* in steps-mode what is + // also rare. IF that should make problems reactivation of that case + // for the default case below is possible. main reason is that speed + // for direct rendering in cairo is much better. + processFillGradientPrimitive2D_radial_elliptical(rFillGradientPrimitive2D); + break; + } + case css::awt::GradientStyle_SQUARE: + case css::awt::GradientStyle_RECT: + { + // use specialized renderer for this cases - square, rect + // NOTE: *NO* support for FillGradientAlpha here. it is anyways + // hard to map these to direct rendering, but to do so the four + // trapezoids/sides are 'stitched' together, so painting RGBA + // directly will make the overlaps look bad and like errors. + // Anyways, these gradient types are only our internal heritage + // and rendering them directly is already much faster, will be okay. + processFillGradientPrimitive2D_square_rect(rFillGradientPrimitive2D); + break; + } + default: + { + // NOTE: All cases are covered above, but keep this as fallback, + // so it is possible anytime to exclude one of the cases above again + // and go back to decomposed version - just in case... + processFillGradientPrimitive2D_fallback_decompose(rFillGradientPrimitive2D); + break; + } + } + } + + if (bTemporaryGrayColorModifier) + // cleanup temporary BColorModifier + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::processPolyPolygonRGBAPrimitive2D( + const primitive2d::PolyPolygonRGBAPrimitive2D& rPolyPolygonRGBAPrimitive2D) +{ + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::NoFill) + // NoFill wanted, done + return; + + const basegfx::BColor aFillColor(getFillColor(rPolyPolygonRGBAPrimitive2D.getBColor())); + + if (!rPolyPolygonRGBAPrimitive2D.hasTransparency()) + { + // do what CairoPixelProcessor2D::processPolyPolygonColorPrimitive2D does + paintPolyPolygonRGBA(rPolyPolygonRGBAPrimitive2D.getB2DPolyPolygon(), aFillColor); + return; + } + + // draw with alpha directly + paintPolyPolygonRGBA(rPolyPolygonRGBAPrimitive2D.getB2DPolyPolygon(), aFillColor, + rPolyPolygonRGBAPrimitive2D.getTransparency()); +} + +void CairoPixelProcessor2D::processPolyPolygonAlphaGradientPrimitive2D( + const primitive2d::PolyPolygonAlphaGradientPrimitive2D& rPolyPolygonAlphaGradientPrimitive2D) +{ + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::NoFill) + // NoFill wanted, done + return; + + const basegfx::B2DPolyPolygon& rPolyPolygon( + rPolyPolygonAlphaGradientPrimitive2D.getB2DPolyPolygon()); + if (0 == rPolyPolygon.count()) + { + // no geometry, done + return; + } + + basegfx::BColor aFillColor(getFillColor(rPolyPolygonAlphaGradientPrimitive2D.getBColor())); + aFillColor = maBColorModifierStack.getModifiedColor(aFillColor); + + const attribute::FillGradientAttribute& rAlphaGradient( + rPolyPolygonAlphaGradientPrimitive2D.getAlphaGradient()); + if (rAlphaGradient.isDefault()) + { + // default is a single ColorStop at 0.0 with black (0, 0, 0). The + // luminance is then 0.0, too -> not transparent at all + paintPolyPolygonRGBA(rPolyPolygon, aFillColor); + return; + } + + basegfx::BColor aSingleColor; + const basegfx::BColorStops& rAlphaStops(rAlphaGradient.getColorStops()); + if (rAlphaStops.isSingleColor(aSingleColor)) + { + // draw with alpha directly + paintPolyPolygonRGBA(rPolyPolygon, aFillColor, aSingleColor.luminance()); + return; + } + + const css::awt::GradientStyle aStyle(rAlphaGradient.getStyle()); + if (css::awt::GradientStyle_SQUARE == aStyle || css::awt::GradientStyle_RECT == aStyle) + { + // direct paint cannot be used for these styles since they get 'stitched' + // by multiple parts, so *need* single alpha for multiple pieces, go + // with decompose/recursion + process(rPolyPolygonAlphaGradientPrimitive2D); + return; + } + + // render as FillGradientPrimitive2D. The idea is to create BColorStops + // with the same number of entries, but all the same color, using the + // polygon's target fill color, so we can directly paint gradients as + // RGBA in Cairo + basegfx::BColorStops aColorStops; + + // create ColorStops at same stops but single color + aColorStops.reserve(rAlphaStops.size()); + for (const auto& entry : rAlphaStops) + aColorStops.emplace_back(entry.getStopOffset(), aFillColor); + + // create FillGradient using that single-color ColorStops + const attribute::FillGradientAttribute aFillGradient( + rAlphaGradient.getStyle(), rAlphaGradient.getBorder(), rAlphaGradient.getOffsetX(), + rAlphaGradient.getOffsetY(), rAlphaGradient.getAngle(), aColorStops, + rAlphaGradient.getSteps()); + + // create temporary FillGradientPrimitive2D, but do not forget + // to embed to MaskPrimitive2D to get the PolyPolygon form + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + const primitive2d::Primitive2DContainer aContainerMaskedFillGradient{ + rtl::Reference<primitive2d::MaskPrimitive2D>(new primitive2d::MaskPrimitive2D( + rPolyPolygon, + primitive2d::Primitive2DContainer{ rtl::Reference<primitive2d::FillGradientPrimitive2D>( + new primitive2d::FillGradientPrimitive2D(aRange, // OutputRange + aRange, // DefinitionRange + aFillGradient, &rAlphaGradient)) })) + }; + + // render this. Use container to not trigger decompose for temporary content + process(aContainerMaskedFillGradient); +} + +void CairoPixelProcessor2D::processBitmapAlphaPrimitive2D( + const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaPrimitive2D) +{ + constexpr DrawModeFlags BITMAP(DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap + | DrawModeFlags::GrayBitmap); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + const bool bDrawModeFlagsUsed(aDrawModeFlags & BITMAP); + + if (bDrawModeFlagsUsed) + { + if (aDrawModeFlags & DrawModeFlags::BlackBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0, 0, 0))); + maBColorModifierStack.push(aBColorModifier); + } + else if (aDrawModeFlags & DrawModeFlags::WhiteBitmap) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(1, 1, 1))); + maBColorModifierStack.push(aBColorModifier); + } + else // DrawModeFlags::GrayBitmap + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + } + + if (!rBitmapAlphaPrimitive2D.hasTransparency()) + { + // do what CairoPixelProcessor2D::processPolyPolygonColorPrimitive2D does + paintBitmapAlpha(rBitmapAlphaPrimitive2D.getBitmap(), + rBitmapAlphaPrimitive2D.getTransform()); + } + else + { + // draw with alpha directly + paintBitmapAlpha(rBitmapAlphaPrimitive2D.getBitmap(), + rBitmapAlphaPrimitive2D.getTransform(), + rBitmapAlphaPrimitive2D.getTransparency()); + } + + if (bDrawModeFlagsUsed) + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::processTextSimplePortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rCandidate) +{ + if (SAL_LIKELY(mbRenderSimpleTextDirect)) + { + renderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate, nullptr); + } + else + { + process(rCandidate); + } +} + +void CairoPixelProcessor2D::processTextDecoratedPortionPrimitive2D( + const primitive2d::TextDecoratedPortionPrimitive2D& rCandidate) +{ + if (SAL_LIKELY(mbRenderDecoratedTextDirect)) + { + if (!rCandidate.getOrCreateBrokenUpText().empty()) + { + // if BrokenUpText/WordLineMode is used, go into recursion + // with single snippets + process(rCandidate.getOrCreateBrokenUpText()); + return; + } + + renderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate, &rCandidate); + } + else + { + process(rCandidate); + } +} + +void CairoPixelProcessor2D::renderTextBackground( + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate, double fAscent, + double fDescent, const basegfx::B2DHomMatrix& rTransform, double fTextWidth) +{ + cairo_save(mpRT); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, rTransform.a(), rTransform.b(), rTransform.c(), rTransform.d(), + rTransform.e(), rTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + basegfx::BColor aFillColor(getFillColor(rTextCandidate.getTextFillColor().getBColor())); + aFillColor = maBColorModifierStack.getModifiedColor(aFillColor); + cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue()); + cairo_rectangle(mpRT, 0.0, -fAscent, fTextWidth, fAscent + fDescent); + cairo_fill(mpRT); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::renderSalLayout(const std::unique_ptr<SalLayout>& rSalLayout, + const basegfx::BColor& rTextColor, + const basegfx::B2DHomMatrix& rTransform, + bool bAntiAliase) const +{ + cairo_save(mpRT); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, rTransform.a(), rTransform.b(), rTransform.c(), rTransform.d(), + rTransform.e(), rTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + rSalLayout->drawSalLayout(mpRT, rTextColor, bAntiAliase); + cairo_restore(mpRT); +} + +void CairoPixelProcessor2D::renderTextDecorationWithOptionalTransformAndColor( + const primitive2d::TextDecoratedPortionPrimitive2D& rDecoratedCandidate, + const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans, + const basegfx::B2DHomMatrix* pOptionalObjectTransform, const basegfx::BColor* pReplacementColor) +{ + // get decorations from Primitive (using original TextTransform), + // guaranteed the same visualization as a decomposition would create + const primitive2d::Primitive2DContainer& rDecorationGeometryContent( + rDecoratedCandidate.getOrCreateDecorationGeometryContent( + rDecTrans, rDecoratedCandidate.getText(), rDecoratedCandidate.getTextPosition(), + rDecoratedCandidate.getTextLength(), rDecoratedCandidate.getDXArray())); + + if (rDecorationGeometryContent.empty()) + { + // no decoration, done + return; + } + + // modify ColorStack as needed - if needed + if (nullptr != pReplacementColor) + maBColorModifierStack.push( + std::make_shared<basegfx::BColorModifier_replace>(*pReplacementColor)); + + // modify transformation as needed - if needed + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + if (nullptr != pOptionalObjectTransform) + { + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(*pOptionalObjectTransform); + setViewInformation2D(aViewInformation2D); + } + + // render primitives + process(rDecorationGeometryContent); + + // restore mods + if (nullptr != pOptionalObjectTransform) + setViewInformation2D(aLastViewInformation2D); + if (nullptr != pReplacementColor) + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate, + const primitive2d::TextDecoratedPortionPrimitive2D* pDecoratedCandidate) +{ + primitive2d::TextLayouterDevice aTextLayouter; + rTextCandidate.createTextLayouter(aTextLayouter); + std::unique_ptr<SalLayout> pSalLayout(rTextCandidate.createSalLayout(aTextLayouter)); + + if (!pSalLayout) + { + // got no layout, error. use decompose as fallback + process(rTextCandidate); + return; + } + + // prepare local transformations + basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans( + rTextCandidate.getTextTransform()); + const basegfx::B2DHomMatrix aObjTransformWithoutScale( + basegfx::utils::createShearXRotateTranslateB2DHomMatrix( + aDecTrans.getShearX(), aDecTrans.getRotate(), aDecTrans.getTranslate())); + const basegfx::B2DHomMatrix aFullTextTransform( + getViewInformation2D().getObjectToViewTransformation() * aObjTransformWithoutScale); + + if (!rTextCandidate.getTextFillColor().IsTransparent()) + { + // render TextBackground first -> casts no shadow itself, so do independent of + // text shadow being activated + double fAscent(aTextLayouter.getFontAscent()); + double fDescent(aTextLayouter.getFontDescent()); + + if (nullptr != pDecoratedCandidate + && primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE + != pDecoratedCandidate->getTextEmphasisMark()) + { + if (pDecoratedCandidate->getEmphasisMarkAbove()) + fAscent += aTextLayouter.getTextHeight() * (250.0 / 1000.0); + if (pDecoratedCandidate->getEmphasisMarkBelow()) + fDescent += aTextLayouter.getTextHeight() * (250.0 / 1000.0); + } + + renderTextBackground(rTextCandidate, fAscent, fDescent, aFullTextTransform, + pSalLayout->GetTextWidth()); + } + + // get TextColor early, may have to be modified + basegfx::BColor aTextColor(getTextColor(rTextCandidate.getFontColor())); + + if (rTextCandidate.hasShadow()) + { + // Text shadow is constant, relative to font size, *not* rotated with + // text (always from top-left!) + static const double fFactor(1.0 / 24.0); + const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor); + + // see ::ImplDrawSpecialText -> no longer simple fixed color + const basegfx::BColor aBlack(0.0, 0.0, 0.0); + basegfx::BColor aShadowColor(aBlack); + if (aBlack == aTextColor || aTextColor.luminance() < (8.0 / 255.0)) + aShadowColor = COL_LIGHTGRAY.getBColor(); + aShadowColor = maBColorModifierStack.getModifiedColor(aShadowColor); + + // create shadow offset + const basegfx::B2DHomMatrix aShadowTransform( + basegfx::utils::createTranslateB2DHomMatrix(fTextShadowOffset, fTextShadowOffset)); + const basegfx::B2DHomMatrix aShadowFullTextTransform( + // right to left: 1st the ObjTrans, then the shadow offset, last ObjToView. That way + // the shadow is always from top-left, independent of text rotation. Independent from + // thinking about if that is wanted (shadow direction *could* rotate with the text) + // this is what the office currently does -> do *not* change visualization (!) + getViewInformation2D().getObjectToViewTransformation() * aShadowTransform + * aObjTransformWithoutScale); + + // render text as shadow + renderSalLayout(pSalLayout, aShadowColor, aShadowFullTextTransform, + getViewInformation2D().getUseAntiAliasing()); + + if (rTextCandidate.hasTextDecoration()) + { + const basegfx::B2DHomMatrix aTransform(getViewInformation2D().getObjectTransformation() + * aShadowTransform); + renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans, + &aTransform, &aShadowColor); + } + } + if (rTextCandidate.hasOutline()) + { + // render as outline + aTextColor = maBColorModifierStack.getModifiedColor(aTextColor); + basegfx::B2DHomMatrix aInvViewTransform; + + // discrete offsets defined here to easily allow to change them, + // e.g. if more 'fat' outline is wanted, it may be increased to 1.5 + constexpr double fZero(0.0); + constexpr double fPlus(1.0); + constexpr double fMinus(-1.0); + + static constexpr std::array<std::pair<double, double>, 8> offsets{ + std::pair<double, double>{ fMinus, fMinus }, std::pair<double, double>{ fZero, fMinus }, + std::pair<double, double>{ fPlus, fMinus }, std::pair<double, double>{ fMinus, fZero }, + std::pair<double, double>{ fPlus, fZero }, std::pair<double, double>{ fMinus, fPlus }, + std::pair<double, double>{ fZero, fPlus }, std::pair<double, double>{ fPlus, fPlus } + }; + + if (rTextCandidate.hasTextDecoration()) + { + // to use discrete offset (pixels) we will need the back-transform from + // discrete view coordinates to 'world' coordinates (logic view coordinates), + // this is the inverse ViewTransformation. + // NOTE: Alternatively we could calculate the lengths for fPlus/fMinus in + // logic view coordinates, but would need to create another B2DHomMatrix and + // to do it correct would need to handle two vectors holding the directions, + // else - if ever someone will rotate/shear that transformation - it would + // break + aInvViewTransform = getViewInformation2D().getViewTransformation(); + aInvViewTransform.invert(); + } + + for (const auto& offset : offsets) + { + const basegfx::B2DHomMatrix aDiscreteOffset( + basegfx::utils::createTranslateB2DHomMatrix(offset.first, offset.second)); + renderSalLayout(pSalLayout, aTextColor, aDiscreteOffset * aFullTextTransform, + getViewInformation2D().getUseAntiAliasing()); + if (rTextCandidate.hasTextDecoration()) + { + basegfx::B2DHomMatrix aTransform( + aInvViewTransform * aDiscreteOffset + * getViewInformation2D().getObjectToViewTransformation()); + renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans, + &aTransform); + } + } + + // at (center, center) paint in COL_WHITE + aTextColor = maBColorModifierStack.getModifiedColor(COL_WHITE.getBColor()); + renderSalLayout(pSalLayout, aTextColor, aFullTextTransform, + getViewInformation2D().getUseAntiAliasing()); + if (rTextCandidate.hasTextDecoration()) + { + renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans, + nullptr, &aTextColor); + } + + // paint is complete, Outline and TextRelief cannot be combined, return + return; + } + + if (rTextCandidate.hasTextRelief()) + { + // manipulate TextColor for final text paint below (see ::ImplDrawSpecialText) + if (aTextColor == COL_BLACK.getBColor()) + aTextColor = COL_WHITE.getBColor(); + + // relief offset defined here to easily allow to change them + // see ::ImplDrawSpecialText and the comment @ 'nOff += mnDPIX/300' + const bool bEmboss(primitive2d::TEXT_RELIEF_EMBOSSED + == pDecoratedCandidate->getTextRelief()); + constexpr double fReliefOffset(1.1); + const double fOffset(bEmboss ? fReliefOffset : -fReliefOffset); + const basegfx::B2DHomMatrix aDiscreteOffset( + basegfx::utils::createTranslateB2DHomMatrix(fOffset, fOffset)); + + // see aReliefColor in ::ImplDrawSpecialText + basegfx::BColor aReliefColor(COL_LIGHTGRAY.getBColor()); + if (COL_WHITE.getBColor() == aTextColor) + aReliefColor = COL_BLACK.getBColor(); + aReliefColor = maBColorModifierStack.getModifiedColor(aReliefColor); + + // render relief text with offset + renderSalLayout(pSalLayout, aReliefColor, aDiscreteOffset * aFullTextTransform, + getViewInformation2D().getUseAntiAliasing()); + + if (rTextCandidate.hasTextDecoration()) + { + basegfx::B2DHomMatrix aInvViewTransform(getViewInformation2D().getViewTransformation()); + aInvViewTransform.invert(); + const basegfx::B2DHomMatrix aTransform( + aInvViewTransform * aDiscreteOffset + * getViewInformation2D().getObjectToViewTransformation()); + renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans, + &aTransform, &aReliefColor); + } + } + + // render text + aTextColor = maBColorModifierStack.getModifiedColor(aTextColor); + renderSalLayout(pSalLayout, aTextColor, aFullTextTransform, + getViewInformation2D().getUseAntiAliasing()); + + if (rTextCandidate.hasTextDecoration()) + { + // render using same geometry/primitives that a decompose would + // create -> safe to get the same visualization for both + renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans); + } +} + +bool CairoPixelProcessor2D::handleSvgGradientHelper( + const primitive2d::SvgGradientHelper& rCandidate) +{ + // check PolyPolygon to be filled + const basegfx::B2DPolyPolygon& rPolyPolygon(rCandidate.getPolyPolygon()); + + if (!rPolyPolygon.count()) + { + // no PolyPolygon, done + return true; + } + + // calculate visible range + basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange()); + aPolyPolygonRange.transform(getViewInformation2D().getObjectToViewTransformation()); + if (!getDiscreteViewRange(mpRT).overlaps(aPolyPolygonRange)) + { + // not visible, done + return true; + } + + if (!rCandidate.getCreatesContent()) + { + // creates no content, done + return true; + } + + basegfx::BColor aSimpleColor; + bool bDrawSimple(false); + primitive2d::SvgGradientEntryVector::const_reference aEntry( + rCandidate.getGradientEntries().back()); + + constexpr DrawModeFlags SIMPLE_GRADIENT(DrawModeFlags::BlackGradient + | DrawModeFlags::WhiteGradient + | DrawModeFlags::SettingsGradient); + if (getViewInformation2D().getDrawModeFlags() & SIMPLE_GRADIENT) + { + aSimpleColor = getGradientColor(aSimpleColor); + bDrawSimple = true; + } + + if (!bDrawSimple && rCandidate.getSingleEntry()) + { + // only one color entry, fill with last existing color, done + aSimpleColor = aEntry.getColor(); + bDrawSimple = true; + } + + if (bDrawSimple) + { + paintPolyPolygonRGBA(rCandidate.getPolyPolygon(), aSimpleColor, 1.0 - aEntry.getOpacity()); + return true; + } + + return false; +} + +void CairoPixelProcessor2D::processSvgLinearGradientPrimitive2D( + const primitive2d::SvgLinearGradientPrimitive2D& rCandidate) +{ + // check for simple cases, returns if all necessary is already done + if (handleSvgGradientHelper(rCandidate)) + { + // simple case, handled, done + return; + } + + cairo_save(mpRT); + + const bool bTemporaryGrayColorModifier(getViewInformation2D().getDrawModeFlags() + & DrawModeFlags::GrayGradient); + if (bTemporaryGrayColorModifier) + { + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + + // set ObjectToView as regular transformation at CairoContext + const basegfx::B2DHomMatrix aTrans(getViewInformation2D().getObjectToViewTransformation()); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), aTrans.d(), aTrans.e(), + aTrans.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // create pattern using unit coordinates. Unit coordinates here means that + // the transformation provided by the primitive maps the linear gradient + // to (0,0) -> (1,0) at the unified object coordinates, along the unified + // X-Axis + cairo_pattern_t* pPattern(cairo_pattern_create_linear(0, 0, 1, 0)); + + // get pre-defined UnitGradientToObject transformation from primitive + // and invert to get ObjectToUnitGradient transform + basegfx::B2DHomMatrix aObjectToUnitGradient( + rCandidate.createUnitGradientToObjectTransformation()); + aObjectToUnitGradient.invert(); + + // set ObjectToUnitGradient as transformation at gradient - patterns + // need the inverted transformation, see cairo documentation + cairo_matrix_init(&aMatrix, aObjectToUnitGradient.a(), aObjectToUnitGradient.b(), + aObjectToUnitGradient.c(), aObjectToUnitGradient.d(), + aObjectToUnitGradient.e(), aObjectToUnitGradient.f()); + cairo_pattern_set_matrix(pPattern, &aMatrix); + + // add color stops + const primitive2d::SvgGradientEntryVector& rGradientEntries(rCandidate.getGradientEntries()); + + for (const auto& entry : rGradientEntries) + { + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(entry.getColor())); + cairo_pattern_add_color_stop_rgba(pPattern, entry.getOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), entry.getOpacity()); + } + + // set SpreadMethod. Note that we have no SpreadMethod::None because the + // source is SVG and SVG does also not have that (checked that) + switch (rCandidate.getSpreadMethod()) + { + case primitive2d::SpreadMethod::Pad: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_PAD); + break; + case primitive2d::SpreadMethod::Reflect: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REFLECT); + break; + case primitive2d::SpreadMethod::Repeat: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REPEAT); + break; + } + + // get PathGeometry & paint it filed with gradient + cairo_new_path(mpRT); + getOrCreateFillGeometry(mpRT, rCandidate.getPolyPolygon()); + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + + // cleanup + cairo_pattern_destroy(pPattern); + cairo_restore(mpRT); + + if (bTemporaryGrayColorModifier) + // cleanup temporary BColorModifier + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::processSvgRadialGradientPrimitive2D( + const primitive2d::SvgRadialGradientPrimitive2D& rCandidate) +{ + // check for simple cases, returns if all necessary is already done + if (handleSvgGradientHelper(rCandidate)) + { + // simple case, handled, done + return; + } + + cairo_save(mpRT); + + bool bTemporaryGrayColorModifier(false); + if (getViewInformation2D().getDrawModeFlags() & DrawModeFlags::GrayGradient) + { + bTemporaryGrayColorModifier = true; + const basegfx::BColorModifierSharedPtr aBColorModifier( + std::make_shared<basegfx::BColorModifier_gray>()); + maBColorModifierStack.push(aBColorModifier); + } + + // set ObjectToView as regular transformation at CairoContext + const basegfx::B2DHomMatrix aTrans(getViewInformation2D().getObjectToViewTransformation()); + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aTrans.a(), aTrans.b(), aTrans.c(), aTrans.d(), aTrans.e(), + aTrans.f()); + cairo_set_matrix(mpRT, &aMatrix); + + // get pre-defined UnitGradientToObject transformation from primitive + // and invert to get ObjectToUnitGradient transform + basegfx::B2DHomMatrix aObjectToUnitGradient( + rCandidate.createUnitGradientToObjectTransformation()); + aObjectToUnitGradient.invert(); + + // prepare empty FocalVector + basegfx::B2DVector aFocalVector(0.0, 0.0); + + if (rCandidate.isFocalSet()) + { + // FocalPoint is used, create ObjectTransform based on polygon range + const basegfx::B2DRange aPolyRange(rCandidate.getPolyPolygon().getB2DRange()); + const double fPolyWidth(aPolyRange.getWidth()); + const double fPolyHeight(aPolyRange.getHeight()); + const basegfx::B2DHomMatrix aObjectTransform( + basegfx::utils::createScaleTranslateB2DHomMatrix( + fPolyWidth, fPolyHeight, aPolyRange.getMinX(), aPolyRange.getMinY())); + + // get vector, then transform to object coordinates, then to + // UnitGradient coordinates to be in the needed coordinate system + aFocalVector = basegfx::B2DVector(rCandidate.getStart() - rCandidate.getFocal()); + aFocalVector *= aObjectTransform; + aFocalVector *= aObjectToUnitGradient; + } + + // create pattern using unit coordinates. Unit coordinates here means that + // the transformation provided by the primitive maps the radial gradient + // to (0,0) as center, 1.0 as radius - which is the unit circle. The + // FocalPoint (if used) has to be relative to that, so - since unified + // center is at (0, 0), handling as vector is sufficient + cairo_pattern_t* pPattern( + cairo_pattern_create_radial(0, 0, 0, aFocalVector.getX(), aFocalVector.getY(), 1)); + + // set ObjectToUnitGradient as transformation at gradient - patterns + // need the inverted transformation, see cairo documentation + cairo_matrix_init(&aMatrix, aObjectToUnitGradient.a(), aObjectToUnitGradient.b(), + aObjectToUnitGradient.c(), aObjectToUnitGradient.d(), + aObjectToUnitGradient.e(), aObjectToUnitGradient.f()); + cairo_pattern_set_matrix(pPattern, &aMatrix); + + // add color stops + const primitive2d::SvgGradientEntryVector& rGradientEntries(rCandidate.getGradientEntries()); + + for (const auto& entry : rGradientEntries) + { + const basegfx::BColor aColor(maBColorModifierStack.getModifiedColor(entry.getColor())); + cairo_pattern_add_color_stop_rgba(pPattern, entry.getOffset(), aColor.getRed(), + aColor.getGreen(), aColor.getBlue(), entry.getOpacity()); + } + + // set SpreadMethod + switch (rCandidate.getSpreadMethod()) + { + case primitive2d::SpreadMethod::Pad: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_PAD); + break; + case primitive2d::SpreadMethod::Reflect: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REFLECT); + break; + case primitive2d::SpreadMethod::Repeat: + cairo_pattern_set_extend(pPattern, CAIRO_EXTEND_REPEAT); + break; + } + + // get PathGeometry & paint it filed with gradient + cairo_new_path(mpRT); + getOrCreateFillGeometry(mpRT, rCandidate.getPolyPolygon()); + cairo_set_source(mpRT, pPattern); + cairo_fill(mpRT); + + // cleanup + cairo_pattern_destroy(pPattern); + cairo_restore(mpRT); + + if (bTemporaryGrayColorModifier) + // cleanup temporary BColorModifier + maBColorModifierStack.pop(); +} + +void CairoPixelProcessor2D::processControlPrimitive2D( + const primitive2d::ControlPrimitive2D& rControlPrimitive) +{ + // find out if the control is already visualized as a VCL-ChildWindow + bool bControlIsVisibleAsChildWindow(rControlPrimitive.isVisibleAsChildWindow()); + + // tdf#131281 FormControl rendering for Tiled Rendering + if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) + { + // Do force paint when we are in Tiled Renderer and FormControl is 'visible' + bControlIsVisibleAsChildWindow = false; + } + + if (bControlIsVisibleAsChildWindow) + { + // f the control is already visualized as a VCL-ChildWindow it + // does not need to be painted at all + return; + } + + bool bDone(false); + + try + { + if (nullptr != mpTargetOutputDevice) + { + const uno::Reference<awt::XGraphics> xTargetGraphics( + mpTargetOutputDevice->CreateUnoGraphics()); + + if (xTargetGraphics.is()) + { + // Needs to be drawn. Link new graphics and view + const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); + uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); + xControlView->setGraphics(xTargetGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToPixel( + getViewInformation2D().getObjectToViewTransformation() + * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); + + xControlView->draw(basegfx::fround(aTopLeftPixel.getX()), + basegfx::fround(aTopLeftPixel.getY())); + + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + bDone = true; + } + } + } + catch (const uno::Exception&) + { + // #i116763# removing since there is a good alternative when the xControlView + // is not found and it is allowed to happen + // DBG_UNHANDLED_EXCEPTION(); + } + + if (!bDone) + { + // process recursively and use the decomposition as Bitmap + process(rControlPrimitive); + } +} + +void CairoPixelProcessor2D::evaluateCairoCoordinateLimitWorkaround() +{ + static bool bAlreadyCheckedIfNeeded(false); + static bool bIsNeeded(false); + + if (!bAlreadyCheckedIfNeeded) + { + // check once for office runtime: is workaround needed? + bAlreadyCheckedIfNeeded = true; + bIsNeeded = checkCoordinateLimitWorkaroundNeededForUsedCairo(); + } + + if (!bIsNeeded) + { + // we have a working cairo, so workaround is not needed + // and mbCairoCoordinateLimitWorkaroundActive can stay false + return; + } + + // get discrete size (pixels) + basegfx::B2DRange aLogicViewRange(getDiscreteViewRange(mpRT)); + + // transform to world coordinates -> logic view range + basegfx::B2DHomMatrix aInvViewTrans(getViewInformation2D().getViewTransformation()); + aInvViewTrans.invert(); + aLogicViewRange.transform(aInvViewTrans); + + // create 1<<23 CairoCoordinate limit from 24.8 internal format + // and a range fitting to it (just once, this is static) + constexpr double fNumCairoMax(1 << 23); + static const basegfx::B2DRange aNumericalCairoLimit(-fNumCairoMax, -fNumCairoMax, + fNumCairoMax - 1.0, fNumCairoMax - 1.0); + + if (!aLogicViewRange.isEmpty() && !aNumericalCairoLimit.isInside(aLogicViewRange)) + { + // aLogicViewRange is not completely inside region covered by + // 24.8 cairo format, thus workaround is needed, set flag + mbCairoCoordinateLimitWorkaroundActive = true; + } +} + +basegfx::BColor CairoPixelProcessor2D::getLineColor(const basegfx::BColor& rColor) const +{ + constexpr DrawModeFlags LINE(DrawModeFlags::BlackLine | DrawModeFlags::WhiteLine + | DrawModeFlags::GrayLine | DrawModeFlags::SettingsLine); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + + if (!(aDrawModeFlags & LINE)) + return rColor; + + if (aDrawModeFlags & DrawModeFlags::BlackLine) + return basegfx::BColor(0, 0, 0); + + if (aDrawModeFlags & DrawModeFlags::WhiteLine) + return basegfx::BColor(1, 1, 1); + + if (aDrawModeFlags & DrawModeFlags::GrayLine) + { + const double fLuminance(rColor.luminance()); + return basegfx::BColor(fLuminance, fLuminance, fLuminance); + } + + // DrawModeFlags::SettingsLine + if (aDrawModeFlags & DrawModeFlags::SettingsForSelection) + return Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + + return Application::GetSettings().GetStyleSettings().GetWindowTextColor().getBColor(); +} + +basegfx::BColor CairoPixelProcessor2D::getFillColor(const basegfx::BColor& rColor) const +{ + constexpr DrawModeFlags FILL(DrawModeFlags::BlackFill | DrawModeFlags::WhiteFill + | DrawModeFlags::GrayFill | DrawModeFlags::SettingsFill); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + + if (!(aDrawModeFlags & FILL)) + return rColor; + + if (aDrawModeFlags & DrawModeFlags::BlackFill) + return basegfx::BColor(0, 0, 0); + + if (aDrawModeFlags & DrawModeFlags::WhiteFill) + return basegfx::BColor(1, 1, 1); + + if (aDrawModeFlags & DrawModeFlags::GrayFill) + { + const double fLuminance(rColor.luminance()); + return basegfx::BColor(fLuminance, fLuminance, fLuminance); + } + + // DrawModeFlags::SettingsFill + if (aDrawModeFlags & DrawModeFlags::SettingsForSelection) + return Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + + return Application::GetSettings().GetStyleSettings().GetWindowColor().getBColor(); +} + +basegfx::BColor CairoPixelProcessor2D::getTextColor(const basegfx::BColor& rColor) const +{ + constexpr DrawModeFlags TEXT(DrawModeFlags::BlackText | DrawModeFlags::WhiteText + | DrawModeFlags::GrayText | DrawModeFlags::SettingsText); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + + if (!(aDrawModeFlags & TEXT)) + return rColor; + + if (aDrawModeFlags & DrawModeFlags::BlackText) + return basegfx::BColor(0, 0, 0); + + if (aDrawModeFlags & DrawModeFlags::WhiteText) + return basegfx::BColor(1, 1, 1); + + if (aDrawModeFlags & DrawModeFlags::GrayText) + { + const double fLuminance(rColor.luminance()); + return basegfx::BColor(fLuminance, fLuminance, fLuminance); + } + + // DrawModeFlags::SettingsText + if (aDrawModeFlags & DrawModeFlags::SettingsForSelection) + return Application::GetSettings().GetStyleSettings().GetHighlightTextColor().getBColor(); + + return Application::GetSettings().GetStyleSettings().GetWindowTextColor().getBColor(); +} + +basegfx::BColor CairoPixelProcessor2D::getGradientColor(const basegfx::BColor& rColor) const +{ + constexpr DrawModeFlags GRADIENT(DrawModeFlags::BlackGradient | DrawModeFlags::GrayGradient + | DrawModeFlags::WhiteGradient + | DrawModeFlags::SettingsGradient); + const DrawModeFlags aDrawModeFlags(getViewInformation2D().getDrawModeFlags()); + + if (!(aDrawModeFlags & GRADIENT)) + return rColor; + + if (aDrawModeFlags & DrawModeFlags::BlackGradient) + return basegfx::BColor(0, 0, 0); + + if (aDrawModeFlags & DrawModeFlags::WhiteGradient) + return basegfx::BColor(1, 1, 1); + + if (aDrawModeFlags & DrawModeFlags::GrayGradient) + { + const double fLuminance(rColor.luminance()); + return basegfx::BColor(fLuminance, fLuminance, fLuminance); + } + + // DrawModeFlags::SettingsGradient + if (aDrawModeFlags & DrawModeFlags::SettingsForSelection) + return Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor(); + + return Application::GetSettings().GetStyleSettings().GetWindowColor().getBColor(); +} + +void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + const cairo_status_t aStart(cairo_status(mpRT)); + + switch (rCandidate.getPrimitive2DID()) + { + // geometry that *has* to be processed + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: + { + processBitmapPrimitive2D( + static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: + { + processPointArrayPrimitive2D( + static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: + { + processPolygonHairlinePrimitive2D( + static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: + { + processPolyPolygonColorPrimitive2D( + static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); + break; + } + // embedding/groups that *have* to be processed + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: + { + processTransparencePrimitive2D( + static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: + { + processInvertPrimitive2D( + static_cast<const primitive2d::InvertPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D: + { + processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: + { + processModifiedColorPrimitive2D( + static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: + { + processTransformPrimitive2D( + static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); + break; + } + + // geometry that *may* be processed due to being able to do it better + // then using the decomposition + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: + { + processUnifiedTransparencePrimitive2D( + static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: + { + processMarkerArrayPrimitive2D( + static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: + { + processBackgroundColorPrimitive2D( + static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + processPolygonStrokePrimitive2D( + static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D: + { + processLineRectanglePrimitive2D( + static_cast<const primitive2d::LineRectanglePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D: + { + processFilledRectanglePrimitive2D( + static_cast<const primitive2d::FilledRectanglePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D: + { + processSingleLinePrimitive2D( + static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: + { + processFillGraphicPrimitive2D( + static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D: + { + processFillGradientPrimitive2D( + static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONRGBAPRIMITIVE2D: + { + processPolyPolygonRGBAPrimitive2D( + static_cast<const primitive2d::PolyPolygonRGBAPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D: + { + processBitmapAlphaPrimitive2D( + static_cast<const primitive2d::BitmapAlphaPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + { + processPolyPolygonAlphaGradientPrimitive2D( + static_cast<const primitive2d::PolyPolygonAlphaGradientPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: + { + processTextSimplePortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: + { + processTextDecoratedPortionPrimitive2D( + static_cast<const primitive2d::TextDecoratedPortionPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D: + { + processSvgLinearGradientPrimitive2D( + static_cast<const primitive2d::SvgLinearGradientPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D: + { + processSvgRadialGradientPrimitive2D( + static_cast<const primitive2d::SvgRadialGradientPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: + { + processControlPrimitive2D( + static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); + break; + } + + // continue with decompose + default: + { + SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString( + rCandidate.getPrimitive2DID())); + // process recursively + process(rCandidate); + break; + } + } + + const cairo_status_t aEnd(cairo_status(mpRT)); + + if (aStart != aEnd) + { + SAL_WARN("drawinglayer", "CairoSDPR: Cairo status problem (!)"); + } +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/drawinglayer/source/processor2d/contourextractor2d.cxx b/drawinglayer/source/processor2d/contourextractor2d.cxx index 8abec2f50f9d..94d6b109ecfb 100644 --- a/drawinglayer/source/processor2d/contourextractor2d.cxx +++ b/drawinglayer/source/processor2d/contourextractor2d.cxx @@ -19,7 +19,7 @@ #include <drawinglayer/processor2d/contourextractor2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -30,16 +30,12 @@ #include <drawinglayer/primitive2d/sceneprimitive2d.hxx> -using namespace com::sun::star; - - namespace drawinglayer::processor2d { ContourExtractor2D::ContourExtractor2D( const geometry::ViewInformation2D& rViewInformation, bool bExtractFillOnly) : BaseProcessor2D(rViewInformation), - maExtractedContour(), mbExtractFillOnly(bExtractFillOnly) { } @@ -124,20 +120,15 @@ namespace drawinglayer::processor2d const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // create new local ViewInformation2D - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), - getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), - getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process content process(rTransformCandidate.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); break; } diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx new file mode 100644 index 000000000000..82f51fdc4356 --- /dev/null +++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx @@ -0,0 +1,2177 @@ +/* -*- 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> + +// win-specific +#include <prewin.h> +#include <d2d1.h> +#include <d2d1_1.h> +#include <postwin.h> + +#include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx> +#include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx> +#include <sal/log.hxx> +#include <vcl/alpha.hxx> +#include <vcl/outdev.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/Tools.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/invertprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/converters.hxx> +#include <basegfx/curve/b2dcubicbezier.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/utils/systemdependentdata.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/svapp.hxx> + +using namespace com::sun::star; + +namespace +{ +class ID2D1GlobalFactoryProvider +{ + sal::systools::COMReference<ID2D1Factory> mpD2DFactory; + +public: + ID2D1GlobalFactoryProvider() + : mpD2DFactory(nullptr) + { + const HRESULT hr(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), nullptr, + reinterpret_cast<void**>(&mpD2DFactory))); + + if (!SUCCEEDED(hr)) + mpD2DFactory.clear(); + } + + sal::systools::COMReference<ID2D1Factory>& getID2D1Factory() { return mpD2DFactory; } +}; + +ID2D1GlobalFactoryProvider aID2D1GlobalFactoryProvider; + +class ID2D1GlobalRenderTargetProvider +{ + sal::systools::COMReference<ID2D1DCRenderTarget> mpID2D1DCRenderTarget; + +public: + ID2D1GlobalRenderTargetProvider() + : mpID2D1DCRenderTarget() + { + } + + sal::systools::COMReference<ID2D1DCRenderTarget>& getID2D1DCRenderTarget() + { + if (!mpID2D1DCRenderTarget && aID2D1GlobalFactoryProvider.getID2D1Factory()) + { + const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT)); + + const HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget( + &aRTProps, &mpID2D1DCRenderTarget)); + + // interestingly this ID2D1DCRenderTarget already works and can hold + // created ID2D1Bitmap(s) in RenderTarget-specific form, *without* + // any call to "BindDC", thus *without* the need of a real HDC - nice :-) + // When that would be needed, Application::GetDefaultDevice() would need + // to have a HDC that is valid during LO's lifetime. + + if (!SUCCEEDED(hr)) + mpID2D1DCRenderTarget.clear(); + } + + return mpID2D1DCRenderTarget; + } +}; + +ID2D1GlobalRenderTargetProvider aID2D1GlobalRenderTargetProvider; + +class SystemDependentData_ID2D1PathGeometry : public basegfx::SystemDependentData +{ +private: + sal::systools::COMReference<ID2D1PathGeometry> mpID2D1PathGeometry; + +public: + SystemDependentData_ID2D1PathGeometry( + sal::systools::COMReference<ID2D1PathGeometry>& rID2D1PathGeometry) + : basegfx::SystemDependentData(Application::GetSystemDependentDataManager(), + basegfx::SDD_Type::SDDType_ID2D1PathGeometry) + , mpID2D1PathGeometry(rID2D1PathGeometry) + { + } + + const sal::systools::COMReference<ID2D1PathGeometry>& getID2D1PathGeometry() const + { + return mpID2D1PathGeometry; + } + virtual sal_Int64 estimateUsageInBytes() const override; +}; + +sal_Int64 SystemDependentData_ID2D1PathGeometry::estimateUsageInBytes() const +{ + sal_Int64 aRetval(0); + + if (getID2D1PathGeometry()) + { + UINT32 nCount(0); + const HRESULT hr(getID2D1PathGeometry()->GetSegmentCount(&nCount)); + + if (SUCCEEDED(hr)) + { + // without completely receiving and tracing the GeometrySink + // do a rough estimation - each segment is 2D, so has two doubles. + // Some are beziers, so add some guessed buffer for two additional + // control points + aRetval = static_cast<sal_Int64>(nCount) * (6 * sizeof(double)); + } + } + + return aRetval; +} + +basegfx::B2DPoint impPixelSnap(const basegfx::B2DPolygon& rPolygon, + const drawinglayer::geometry::ViewInformation2D& rViewInformation, + sal_uInt32 nIndex) +{ + const sal_uInt32 nCount(rPolygon.count()); + + // get the data + const basegfx::B2ITuple aPrevTuple( + basegfx::fround(rViewInformation.getObjectToViewTransformation() + * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount))); + const basegfx::B2DPoint aCurrPoint(rViewInformation.getObjectToViewTransformation() + * rPolygon.getB2DPoint(nIndex)); + const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint)); + const basegfx::B2ITuple aNextTuple( + basegfx::fround(rViewInformation.getObjectToViewTransformation() + * rPolygon.getB2DPoint((nIndex + 1) % nCount))); + + // get the states + const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX()); + const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX()); + const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY()); + const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY()); + const bool bSnapX(bPrevVertical || bNextVertical); + const bool bSnapY(bPrevHorizontal || bNextHorizontal); + + if (bSnapX || bSnapY) + { + basegfx::B2DPoint aSnappedPoint(bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(), + bSnapY ? aCurrTuple.getY() : aCurrPoint.getY()); + + aSnappedPoint *= rViewInformation.getInverseObjectToViewTransformation(); + + return aSnappedPoint; + } + + return rPolygon.getB2DPoint(nIndex); +} + +void addB2DPolygonToPathGeometry(sal::systools::COMReference<ID2D1GeometrySink>& rSink, + const basegfx::B2DPolygon& rPolygon, + const drawinglayer::geometry::ViewInformation2D* pViewInformation) +{ + const sal_uInt32 nPointCount(rPolygon.count()); + const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1); + basegfx::B2DCubicBezier aEdge; + + for (sal_uInt32 a(0); a < nEdgeCount; a++) + { + rPolygon.getBezierSegment(a, aEdge); + + const basegfx::B2DPoint aEndPoint( + nullptr == pViewInformation + ? aEdge.getEndPoint() + : impPixelSnap(rPolygon, *pViewInformation, (a + 1) % nPointCount)); + + if (aEdge.isBezier()) + { + rSink->AddBezier( + D2D1::BezierSegment(D2D1::Point2F(aEdge.getControlPointA().getX(), + aEdge.getControlPointA().getY()), //C1 + D2D1::Point2F(aEdge.getControlPointB().getX(), + aEdge.getControlPointB().getY()), //c2 + D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY()))); //end + } + else + { + rSink->AddLine(D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY())); + } + } +} + +std::shared_ptr<SystemDependentData_ID2D1PathGeometry> +getOrCreatePathGeometry(const basegfx::B2DPolygon& rPolygon, + const drawinglayer::geometry::ViewInformation2D& rViewInformation) +{ + // try to access buffered data + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry( + rPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>( + basegfx::SDD_Type::SDDType_ID2D1PathGeometry)); + + if (pSystemDependentData_ID2D1PathGeometry) + { + if (rViewInformation.getPixelSnapHairline()) + { + // do not buffer when PixelSnap is active + pSystemDependentData_ID2D1PathGeometry.reset(); + } + else + { + // use and return buffered data + return pSystemDependentData_ID2D1PathGeometry; + } + } + + sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry; + HRESULT hr( + aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry)); + const sal_uInt32 nPointCount(rPolygon.count()); + + if (SUCCEEDED(hr) && nPointCount) + { + sal::systools::COMReference<ID2D1GeometrySink> pSink; + hr = pID2D1PathGeometry->Open(&pSink); + + if (SUCCEEDED(hr) && pSink) + { + const basegfx::B2DPoint aStart(rViewInformation.getPixelSnapHairline() + ? rPolygon.getB2DPoint(0) + : impPixelSnap(rPolygon, rViewInformation, 0)); + + pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()), + D2D1_FIGURE_BEGIN_HOLLOW); + addB2DPolygonToPathGeometry(pSink, rPolygon, &rViewInformation); + pSink->EndFigure(rPolygon.isClosed() ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); + pSink->Close(); + } + } + + // add to buffering mechanism + if (pID2D1PathGeometry) + { + if (rViewInformation.getPixelSnapHairline() || nPointCount <= 4) + { + // do not buffer when PixelSnap is active or small polygon + return std::make_shared<SystemDependentData_ID2D1PathGeometry>(pID2D1PathGeometry); + } + else + { + return rPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>( + pID2D1PathGeometry); + } + } + + return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>(); +} + +std::shared_ptr<SystemDependentData_ID2D1PathGeometry> +getOrCreateFillGeometry(const basegfx::B2DPolyPolygon& rPolyPolygon) +{ + // try to access buffered data + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry( + rPolyPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>( + basegfx::SDD_Type::SDDType_ID2D1PathGeometry)); + + if (pSystemDependentData_ID2D1PathGeometry) + { + // use and return buffered data + return pSystemDependentData_ID2D1PathGeometry; + } + + sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry; + HRESULT hr( + aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry)); + const sal_uInt32 nCount(rPolyPolygon.count()); + + if (SUCCEEDED(hr) && nCount) + { + sal::systools::COMReference<ID2D1GeometrySink> pSink; + hr = pID2D1PathGeometry->Open(&pSink); + + if (SUCCEEDED(hr) && pSink) + { + for (sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B2DPolygon& rPolygon(rPolyPolygon.getB2DPolygon(a)); + const sal_uInt32 nPointCount(rPolygon.count()); + + if (nPointCount) + { + const basegfx::B2DPoint aStart(rPolygon.getB2DPoint(0)); + + pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()), + D2D1_FIGURE_BEGIN_FILLED); + addB2DPolygonToPathGeometry(pSink, rPolygon, nullptr); + pSink->EndFigure(D2D1_FIGURE_END_CLOSED); + } + } + + pSink->Close(); + } + } + + // add to buffering mechanism + if (pID2D1PathGeometry) + { + return rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>( + pID2D1PathGeometry); + } + + return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>(); +} + +class SystemDependentData_ID2D1Bitmap : public basegfx::SystemDependentData +{ +private: + sal::systools::COMReference<ID2D1Bitmap> mpD2DBitmap; + +public: + SystemDependentData_ID2D1Bitmap(sal::systools::COMReference<ID2D1Bitmap>& rD2DBitmap) + : basegfx::SystemDependentData(Application::GetSystemDependentDataManager(), + basegfx::SDD_Type::SDDType_ID2D1Bitmap) + , mpD2DBitmap(rD2DBitmap) + { + } + + const sal::systools::COMReference<ID2D1Bitmap>& getID2D1Bitmap() const { return mpD2DBitmap; } + + virtual sal_Int64 estimateUsageInBytes() const override; +}; + +sal_Int64 SystemDependentData_ID2D1Bitmap::estimateUsageInBytes() const +{ + sal_Int64 aRetval(0); + + if (getID2D1Bitmap()) + { + // use factor 4 for RGBA_8 as estimation + const D2D1_SIZE_U aSizePixel(getID2D1Bitmap()->GetPixelSize()); + aRetval = static_cast<sal_Int64>(aSizePixel.width) + * static_cast<sal_Int64>(aSizePixel.height) * 4; + } + + return aRetval; +} + +sal::systools::COMReference<ID2D1Bitmap> createB2DBitmap(const Bitmap& rBitmap) +{ + const Size& rSizePixel(rBitmap.GetSizePixel()); + const bool bAlpha(rBitmap.HasAlpha()); + const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height()); + std::unique_ptr<sal_uInt32[]> aData(new sal_uInt32[nPixelCount]); + sal_uInt32* pTarget = aData.get(); + + { + BitmapScopedReadAccess pReadAccess(rBitmap); + const tools::Long nHeight(pReadAccess->Height()); + const tools::Long nWidth(pReadAccess->Width()); + + for (tools::Long y = 0; y < nHeight; ++y) + { + for (tools::Long x = 0; x < nWidth; ++x) + { + const BitmapColor aColor(pReadAccess->GetColor(y, x)); + *pTarget++ = sal_uInt32(aColor); + } + } + } + + // use GlobalRenderTarget to allow usage combined with + // the Direct2D CreateSharedBitmap-mechanism. This is needed + // since ID2D1Bitmap is a ID2D1RenderTarget-dependent resource + // and thus - in principle - would have to be re-created for + // *each* new ID2D1RenderTarget, that means for *each* new + // target HDC, resp. OutputDevice + sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap; + + if (aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget()) + { + const HRESULT hr(aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget()->CreateBitmap( + D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0], + rSizePixel.Width() * sizeof(sal_uInt32), + D2D1::BitmapProperties( + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, // DXGI_FORMAT + bAlpha ? D2D1_ALPHA_MODE_PREMULTIPLIED + : D2D1_ALPHA_MODE_IGNORE)), // D2D1_ALPHA_MODE + &pID2D1Bitmap)); + + if (!SUCCEEDED(hr)) + pID2D1Bitmap.clear(); + } + + return pID2D1Bitmap; +} + +sal::systools::COMReference<ID2D1Bitmap> +getOrCreateB2DBitmap(sal::systools::COMReference<ID2D1RenderTarget>& rRT, const Bitmap& rBitmap) +{ + const basegfx::SystemDependentDataHolder* pHolder(rBitmap.accessSystemDependentDataHolder()); + std::shared_ptr<SystemDependentData_ID2D1Bitmap> pSystemDependentData_ID2D1Bitmap; + + if (nullptr != pHolder) + { + // try to access SystemDependentDataHolder and buffered data + pSystemDependentData_ID2D1Bitmap + = std::static_pointer_cast<SystemDependentData_ID2D1Bitmap>( + pHolder->getSystemDependentData(basegfx::SDD_Type::SDDType_ID2D1Bitmap)); + } + + if (!pSystemDependentData_ID2D1Bitmap) + { + // have to create newly + sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap(createB2DBitmap(rBitmap)); + + if (pID2D1Bitmap) + { + // creation worked, create SystemDependentData_ID2D1Bitmap + pSystemDependentData_ID2D1Bitmap + = std::make_shared<SystemDependentData_ID2D1Bitmap>(pID2D1Bitmap); + + // only add if feasible + if (nullptr != pHolder + && pSystemDependentData_ID2D1Bitmap->calculateCombinedHoldCyclesInSeconds() > 0) + { + basegfx::SystemDependentData_SharedPtr r2(pSystemDependentData_ID2D1Bitmap); + const_cast<basegfx::SystemDependentDataHolder*>(pHolder) + ->addOrReplaceSystemDependentData(r2); + } + } + } + + sal::systools::COMReference<ID2D1Bitmap> pWrappedD2DBitmap; + + if (pSystemDependentData_ID2D1Bitmap) + { + // embed to CreateSharedBitmap, that makes it usable on + // the specified RenderTarget + const HRESULT hr(rRT->CreateSharedBitmap( + __uuidof(ID2D1Bitmap), + static_cast<void*>(pSystemDependentData_ID2D1Bitmap->getID2D1Bitmap()), nullptr, + &pWrappedD2DBitmap)); + + if (!SUCCEEDED(hr)) + pWrappedD2DBitmap.clear(); + } + + return pWrappedD2DBitmap; +} + +// This is a simple local derivation of D2DPixelProcessor2D to be used +// when sub-content needs to be rendered to pixels. Hand over the adapted +// ViewInformation2D, a pixel size and the parent RenderTarget. It will +// locally create and use a ID2D1BitmapRenderTarget to render the stuff +// (you need to call process() with the primitives to be painted of +// course). Then use the local helper getID2D1Bitmap() to access the +// ID2D1Bitmap which was the target of that operation. +class D2DBitmapPixelProcessor2D final : public drawinglayer::processor2d::D2DPixelProcessor2D +{ + // the local ID2D1BitmapRenderTarget + sal::systools::COMReference<ID2D1BitmapRenderTarget> mpBitmapRenderTarget; + +public: + // helper class to create another instance of D2DPixelProcessor2D for + // creating helper-ID2D1Bitmap's for a given ID2D1RenderTarget + D2DBitmapPixelProcessor2D(const drawinglayer::geometry::ViewInformation2D& rViewInformation, + sal_uInt32 nWidth, sal_uInt32 nHeight, + const sal::systools::COMReference<ID2D1RenderTarget>& rParent) + : drawinglayer::processor2d::D2DPixelProcessor2D(rViewInformation) + , mpBitmapRenderTarget() + { + if (0 == nWidth || 0 == nHeight) + { + // no width/height, done + increaseError(); + } + + if (!hasError()) + { + // Allocate compatible RGBA render target + const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(nWidth, nHeight)); + const HRESULT hr(rParent->CreateCompatibleRenderTarget( + nullptr, &aRenderTargetSizePixel, nullptr, + D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &mpBitmapRenderTarget)); + + if (!SUCCEEDED(hr) || nullptr == mpBitmapRenderTarget) + { + // did not work, done + increaseError(); + } + else + { + sal::systools::COMReference<ID2D1RenderTarget> pRT; + mpBitmapRenderTarget->QueryInterface(__uuidof(ID2D1RenderTarget), + reinterpret_cast<void**>(&pRT)); + setRenderTarget(pRT); + } + } + + if (hasRenderTarget()) + { + // set Viewort if none was given. We have a fixed pixel target, s we know the + // exact Viewport to work on + if (getViewInformation2D().getViewport().isEmpty()) + { + drawinglayer::geometry::ViewInformation2D aViewInformation(getViewInformation2D()); + basegfx::B2DRange aViewport(0.0, 0.0, nWidth, nHeight); + basegfx::B2DHomMatrix aInvViewTransform(aViewInformation.getViewTransformation()); + + aInvViewTransform.invert(); + aViewport.transform(aInvViewTransform); + aViewInformation.setViewport(aViewport); + setViewInformation2D(aViewInformation); + } + + // clear as render preparation + getRenderTarget()->BeginDraw(); + getRenderTarget()->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); + getRenderTarget()->EndDraw(); + } + } + + sal::systools::COMReference<ID2D1Bitmap> getID2D1Bitmap() const + { + sal::systools::COMReference<ID2D1Bitmap> pResult; + + // access the resulting bitmap if exists + if (mpBitmapRenderTarget) + { + mpBitmapRenderTarget->GetBitmap(&pResult); + } + + return pResult; + } +}; + +bool createBitmapSubContent(sal::systools::COMReference<ID2D1Bitmap>& rResult, + basegfx::B2DRange& rDiscreteVisibleRange, + const drawinglayer::primitive2d::Primitive2DContainer& rContent, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, + const sal::systools::COMReference<ID2D1RenderTarget>& rRenderTarget) +{ + if (rContent.empty() || !rRenderTarget) + { + // no content or no render target, done + return false; + } + + drawinglayer::processor2d::calculateDiscreteVisibleRange( + rDiscreteVisibleRange, rContent.getB2DRange(rViewInformation2D), rViewInformation2D); + + if (rDiscreteVisibleRange.isEmpty()) + { + // not visible, done + return false; + } + + // Use a temporary second instance of a D2DBitmapPixelProcessor2D with adapted + // ViewInformation2D, it will create the needed ID2D1BitmapRenderTarget + // locally and Clear() it. + drawinglayer::geometry::ViewInformation2D aAdaptedViewInformation2D(rViewInformation2D); + const double fTargetWidth(ceil(rDiscreteVisibleRange.getWidth())); + const double fTargetHeight(ceil(rDiscreteVisibleRange.getHeight())); + + { + // create adapted ViewTransform, needs to be offset in discrete coordinates, + // so multiply from left + basegfx::B2DHomMatrix aAdapted( + basegfx::utils::createTranslateB2DHomMatrix(-rDiscreteVisibleRange.getMinX(), + -rDiscreteVisibleRange.getMinY()) + * rViewInformation2D.getViewTransformation()); + aAdaptedViewInformation2D.setViewTransformation(aAdapted); + + // reset Viewport (world coordinates), so the helper renderer will create it's + // own based on it's given internal discrete size + aAdaptedViewInformation2D.setViewport(basegfx::B2DRange()); + } + + D2DBitmapPixelProcessor2D aSubContentRenderer(aAdaptedViewInformation2D, fTargetWidth, + fTargetHeight, rRenderTarget); + + if (!aSubContentRenderer.valid()) + { + // did not work, done + return false; + } + + // render sub-content recursively + aSubContentRenderer.process(rContent); + + // grab Bitmap & prepare results from RGBA content rendering + rResult = aSubContentRenderer.getID2D1Bitmap(); + return true; +} +} + +namespace drawinglayer::processor2d +{ +D2DPixelProcessor2D::D2DPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation) + , maBColorModifierStack() + , mpRT() + , mnRecursionCounter(0) + , mnErrorCounter(0) +{ +} + +D2DPixelProcessor2D::D2DPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, + HDC aHdc) + : BaseProcessor2D(rViewInformation) + , maBColorModifierStack() + , mpRT() + , mnRecursionCounter(0) + , mnErrorCounter(0) +{ + sal::systools::COMReference<ID2D1DCRenderTarget> pDCRT; + tools::Long aOutWidth(0), aOutHeight(0); + + if (aHdc) + { + aOutWidth = GetDeviceCaps(aHdc, HORZRES); + aOutHeight = GetDeviceCaps(aHdc, VERTRES); + } + + if (aOutWidth > 0 && aOutHeight > 0 && aID2D1GlobalFactoryProvider.getID2D1Factory()) + { + const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT)); + + const HRESULT hr( + aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget(&aRTProps, &pDCRT)); + + if (!SUCCEEDED(hr)) + pDCRT.clear(); + } + + if (pDCRT) + { + const RECT rc( + { 0, 0, o3tl::narrowing<LONG>(aOutWidth), o3tl::narrowing<LONG>(aOutHeight) }); + const HRESULT hr(pDCRT->BindDC(aHdc, &rc)); + + if (!SUCCEEDED(hr)) + pDCRT.clear(); + } + + if (pDCRT) + { + if (rViewInformation.getUseAntiAliasing()) + { + D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + pDCRT->SetAntialiasMode(eAAMode); + } + else + { + D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_ALIASED; + pDCRT->SetAntialiasMode(eAAMode); + } + + // since ID2D1DCRenderTarget depends on the transformation + // set at hdc, be careful and reset it to identity + XFORM aXForm; + aXForm.eM11 = 1.0; + aXForm.eM12 = 0.0; + aXForm.eM21 = 0.0; + aXForm.eM22 = 1.0; + aXForm.eDx = 0.0; + aXForm.eDy = 0.0; + SetWorldTransform(aHdc, &aXForm); + } + + if (pDCRT) + { + sal::systools::COMReference<ID2D1RenderTarget> pRT; + pDCRT->QueryInterface(__uuidof(ID2D1RenderTarget), reinterpret_cast<void**>(&pRT)); + setRenderTarget(pRT); + } + else + { + increaseError(); + } +} + +void D2DPixelProcessor2D::processPolygonHairlinePrimitive2D( + const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) +{ + const basegfx::B2DPolygon& rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon()); + + if (!rPolygon.count()) + { + // no geometry, done + return; + } + + bool bDone(false); + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry( + getOrCreatePathGeometry(rPolygon, getViewInformation2D())); + + if (pSystemDependentData_ID2D1PathGeometry) + { + sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry; + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry( + pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(), + D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e() + fAAOffset, + rObjectToView.f() + fAAOffset), + &pTransformedGeometry)); + + if (SUCCEEDED(hr) && pTransformedGeometry) + { + const basegfx::BColor aHairlineColor( + maBColorModifierStack.getModifiedColor(rPolygonHairlinePrimitive2D.getBColor())); + const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(), + aHairlineColor.getBlue()); + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush); + + if (SUCCEEDED(hr) && pColorBrush) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + // TODO: Unfortunately Direct2D paint of one pixel wide lines does not + // correctly and completely blend 100% over the background. Experimenting + // shows that a value around/slightly below 2.0 is needed which hints that + // alpha blending the half-shifted lines (see fAAOffset above) is involved. + // To get correct blending I try to use just wider hairlines for now. This + // may need to be improved - or balanced (trying sqrt(2) now...) + getRenderTarget()->DrawGeometry(pTransformedGeometry, pColorBrush, 1.44f); + bDone = true; + } + } + } + + if (!bDone) + increaseError(); +} + +bool D2DPixelProcessor2D::drawPolyPolygonColorTransformed( + const basegfx::B2DHomMatrix& rTansformation, const basegfx::B2DPolyPolygon& rPolyPolygon, + const basegfx::BColor& rColor) +{ + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry( + getOrCreateFillGeometry(rPolyPolygon)); + + if (pSystemDependentData_ID2D1PathGeometry) + { + sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry; + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + basegfx::B2DHomMatrix aTansformation(getViewInformation2D().getObjectToViewTransformation() + * rTansformation); + HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry( + pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(), + D2D1::Matrix3x2F(aTansformation.a(), aTansformation.b(), aTansformation.c(), + aTansformation.d(), aTansformation.e() + fAAOffset, + aTansformation.f() + fAAOffset), + &pTransformedGeometry)); + + if (SUCCEEDED(hr) && pTransformedGeometry) + { + const basegfx::BColor aFillColor(maBColorModifierStack.getModifiedColor(rColor)); + const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(), + aFillColor.getBlue()); + + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush); + + if (SUCCEEDED(hr) && pColorBrush) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + getRenderTarget()->FillGeometry(pTransformedGeometry, pColorBrush); + return true; + } + } + } + + return false; +} + +void D2DPixelProcessor2D::processPolyPolygonColorPrimitive2D( + const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) +{ + const basegfx::B2DPolyPolygon& rPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon()); + const sal_uInt32 nCount(rPolyPolygon.count()); + + if (!nCount) + { + // no geometry, done + return; + } + + const bool bDone(drawPolyPolygonColorTransformed(basegfx::B2DHomMatrix(), rPolyPolygon, + rPolyPolygonColorPrimitive2D.getBColor())); + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processBitmapPrimitive2D( + const primitive2d::BitmapPrimitive2D& rBitmapCandidate) +{ + // check if graphic content is inside discrete local ViewPort + if (!getViewInformation2D().getDiscreteViewport().isEmpty()) + { + // calculate logic object range, remember: the helper below will + // transform using getObjectToViewTransformation, so the bitmap-local + // transform would be missing + basegfx::B2DRange aDiscreteVisibleRange(basegfx::B2DRange::getUnitB2DRange()); + aDiscreteVisibleRange.transform(rBitmapCandidate.getTransform()); + + // calculate visible range + calculateDiscreteVisibleRange(aDiscreteVisibleRange, aDiscreteVisibleRange, + getViewInformation2D()); + + if (aDiscreteVisibleRange.isEmpty()) + { + // not visible, done + return; + } + } + + Bitmap aBitmap(rBitmapCandidate.getBitmap()); + + if (aBitmap.IsEmpty() || aBitmap.GetSizePixel().IsEmpty()) + { + // no pixel data, done + return; + } + + if (maBColorModifierStack.count()) + { + // need to apply ColorModifier to Bitmap data + aBitmap = aBitmap.Modify(maBColorModifierStack); + + if (aBitmap.IsEmpty()) + { + // color gets completely replaced, get it (any input works) + const basegfx::BColor aModifiedColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + + // use unit geometry as fallback object geometry. Do *not* + // transform, the below used method will use the already + // correctly initialized local ViewInformation + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + + rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp( + new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), + aModifiedColor)); + + // draw as Polygon, done + processPolyPolygonColorPrimitive2D(*aTemp); + return; + } + } + + bool bDone(false); + sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap( + getOrCreateB2DBitmap(getRenderTarget(), aBitmap)); + + if (pD2DBitmap) + { + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation() + * rBitmapCandidate.getTransform()); + getRenderTarget()->SetTransform(D2D1::Matrix3x2F( + aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(), + aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset)); + + // destinationRectangle is part of transformation above, so use UnitRange + getRenderTarget()->DrawBitmap(pD2DBitmap, D2D1::RectF(0.0, 0.0, 1.0, 1.0)); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_Direct( + const primitive2d::TransparencePrimitive2D& rTransCandidate) +{ + // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface. + // Only then can we use ID2D1Effect/CLSID_D2D1LuminanceToAlpha and it makes + // sense to try to do it this way in this implementation + sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext; + getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext), + reinterpret_cast<void**>(&pID2D1DeviceContext)); + sal::systools::COMReference<ID2D1Bitmap> pRetval; + + if (!pID2D1DeviceContext) + { + // no, done - tell caller to use fallback by returning empty - we have + // not the preconditions for this + return pRetval; + } + + // Release early + pID2D1DeviceContext.clear(); + basegfx::B2DRange aDiscreteVisibleRange; + + if (!createBitmapSubContent(pRetval, aDiscreteVisibleRange, rTransCandidate.getTransparence(), + getViewInformation2D(), getRenderTarget()) + || !pRetval) + { + // return of false means no display needed, return + return pRetval; + } + + // Now we need a target to render this to, using the ID2D1Effect tooling. + // We can directly apply the effect to an alpha-only 8bit target here, + // so create one (no RGBA needed for this). + // We need another render target: I tried to render pInBetweenResult + // to pContent again, but that does not work due to the bitmap + // fetched being probably only an internal reference to the + // ID2D1BitmapRenderTarget, thus it would draw onto itself -> chaos + sal::systools::COMReference<ID2D1BitmapRenderTarget> pContent; + const D2D1_PIXEL_FORMAT aAlphaFormat( + D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT)); + const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(ceil(aDiscreteVisibleRange.getWidth()), + ceil(aDiscreteVisibleRange.getHeight()))); + const HRESULT hr(getRenderTarget()->CreateCompatibleRenderTarget( + nullptr, &aRenderTargetSizePixel, &aAlphaFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, + &pContent)); + + if (SUCCEEDED(hr) && pContent) + { + // try to access ID2D1DeviceContext of that target, we need that *now* + pContent->QueryInterface(__uuidof(ID2D1DeviceContext), + reinterpret_cast<void**>(&pID2D1DeviceContext)); + + if (pID2D1DeviceContext) + { + // create the effect + sal::systools::COMReference<ID2D1Effect> pLuminanceToAlpha; + pID2D1DeviceContext->CreateEffect(CLSID_D2D1LuminanceToAlpha, &pLuminanceToAlpha); + + if (pLuminanceToAlpha) + { + // chain effect stuff together & paint it + pLuminanceToAlpha->SetInput(0, pRetval); + + pID2D1DeviceContext->BeginDraw(); + pID2D1DeviceContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); + pID2D1DeviceContext->DrawImage(pLuminanceToAlpha); + pID2D1DeviceContext->EndDraw(); + + // grab result + pContent->GetBitmap(&pRetval); + } + } + } + + return pRetval; +} + +sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_B2DBitmap( + const primitive2d::TransparencePrimitive2D& rTransCandidate, + const basegfx::B2DRange& rVisibleRange, D2D1_MATRIX_3X2_F& rMaskScale) +{ + // Use this fallback that will also use a pixel processor indirectly, + // but allows to get the AlphaMask as vcl Bitmap using existing tooling + const sal_uInt32 nDiscreteClippedWidth(ceil(rVisibleRange.getWidth())); + const sal_uInt32 nDiscreteClippedHeight(ceil(rVisibleRange.getHeight())); + const sal_uInt32 nMaximumQuadraticPixels(250000); + + // Embed content graphics to TransformPrimitive2D + const basegfx::B2DHomMatrix aAlphaEmbedding( + basegfx::utils::createTranslateB2DHomMatrix(-rVisibleRange.getMinX(), + -rVisibleRange.getMinY()) + * getViewInformation2D().getObjectToViewTransformation()); + const primitive2d::Primitive2DReference xAlphaEmbedRef(new primitive2d::TransformPrimitive2D( + aAlphaEmbedding, + drawinglayer::primitive2d::Primitive2DContainer(rTransCandidate.getTransparence()))); + drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ xAlphaEmbedRef }; + + // use empty ViewInformation to have neutral transformation + const geometry::ViewInformation2D aEmptyViewInformation2D; + + // use new mode to create AlphaChannel (not just AlphaMask) for transparency channel + const AlphaMask aAlpha(::drawinglayer::createAlphaMask( + std::move(xEmbedSeq), aEmptyViewInformation2D, nDiscreteClippedWidth, + nDiscreteClippedHeight, nMaximumQuadraticPixels, true)); + sal::systools::COMReference<ID2D1Bitmap> pRetval; + + if (aAlpha.IsEmpty()) + { + // if we have no content we are done + return pRetval; + } + + // use alpha data to create the ID2D1Bitmap + const Size& rSizePixel(aAlpha.GetSizePixel()); + const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height()); + std::unique_ptr<sal_uInt8[]> aData(new sal_uInt8[nPixelCount]); + sal_uInt8* pTarget = aData.get(); + Bitmap aSrcAlpha(aAlpha.GetBitmap()); + BitmapScopedReadAccess pReadAccess(aSrcAlpha); + const tools::Long nHeight(pReadAccess->Height()); + const tools::Long nWidth(pReadAccess->Width()); + + for (tools::Long y = 0; y < nHeight; ++y) + { + for (tools::Long x = 0; x < nWidth; ++x) + { + const BitmapColor aColor(pReadAccess->GetColor(y, x)); + *pTarget++ = aColor.GetLuminance(); + } + } + + const D2D1_BITMAP_PROPERTIES aBmProps(D2D1::BitmapProperties( + D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED))); + const HRESULT hr(getRenderTarget()->CreateBitmap( + D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0], + rSizePixel.Width() * sizeof(sal_uInt8), &aBmProps, &pRetval)); + + if (!SUCCEEDED(hr) || !pRetval) + { + // did not work, done + return pRetval; + } + + // create needed adapted transformation for alpha brush. + // We may have to take a corrective scaling into account when the + // MaximumQuadraticPixel limit was used/triggered + const Size& rBitmapSizePixel(aAlpha.GetSizePixel()); + + if (static_cast<sal_uInt32>(rBitmapSizePixel.Width()) != nDiscreteClippedWidth + || static_cast<sal_uInt32>(rBitmapSizePixel.Height()) != nDiscreteClippedHeight) + { + // scale in X and Y should be the same (see fReduceFactor in createAlphaMask), + // so adapt numerically to a single scale value, they are integer rounded values + const double fScaleX(static_cast<double>(rBitmapSizePixel.Width()) + / static_cast<double>(nDiscreteClippedWidth)); + const double fScaleY(static_cast<double>(rBitmapSizePixel.Height()) + / static_cast<double>(nDiscreteClippedHeight)); + + const double fScale(1.0 / ((fScaleX + fScaleY) * 0.5)); + rMaskScale = D2D1::Matrix3x2F::Scale(fScale, fScale); + } + + return pRetval; +} + +void D2DPixelProcessor2D::processTransparencePrimitive2D( + const primitive2d::TransparencePrimitive2D& rTransCandidate) +{ + if (rTransCandidate.getChildren().empty()) + { + // no content, done + return; + } + + if (rTransCandidate.getTransparence().empty()) + { + // no mask (so nothing visible), done + return; + } + + // calculate visible range, create only for that range + basegfx::B2DRange aDiscreteVisibleRange; + calculateDiscreteVisibleRange(aDiscreteVisibleRange, + rTransCandidate.getChildren().getB2DRange(getViewInformation2D()), + getViewInformation2D()); + + if (aDiscreteVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + // try to create directly, this needs the current mpRT to be a ID2D1DeviceContext/d2d1_1 + // what is not guaranteed but usually works for more modern windows (after 7) + sal::systools::COMReference<ID2D1Bitmap> pAlphaBitmap(implCreateAlpha_Direct(rTransCandidate)); + D2D1_MATRIX_3X2_F aMaskScale(D2D1::Matrix3x2F::Identity()); + + if (!pAlphaBitmap) + { + // did not work, use more expensive fallback to existing tooling + pAlphaBitmap + = implCreateAlpha_B2DBitmap(rTransCandidate, aDiscreteVisibleRange, aMaskScale); + } + + if (!pAlphaBitmap) + { + // could not create alpha channel, error + increaseError(); + return; + } + + sal::systools::COMReference<ID2D1Layer> pLayer; + HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer)); + bool bDone(false); + + if (SUCCEEDED(hr) && pLayer) + { + sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush; + hr = getRenderTarget()->CreateBitmapBrush(pAlphaBitmap, &pBitmapBrush); + + if (SUCCEEDED(hr) && pBitmapBrush) + { + // apply MaskScale to Brush, maybe used if implCreateAlpha_B2DBitmap was needed + pBitmapBrush->SetTransform(aMaskScale); + + // need to set transform offset for Layer initialization, we work + // in discrete device coordinates + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Translation( + floor(aDiscreteVisibleRange.getMinX()), floor(aDiscreteVisibleRange.getMinY()))); + + getRenderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::Matrix3x2F::Identity(), 1.0, + pBitmapBrush), + pLayer); + + // ... but need to reset to paint content unchanged + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + + // draw content recursively + process(rTransCandidate.getChildren()); + + getRenderTarget()->PopLayer(); + bDone = true; + } + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processUnifiedTransparencePrimitive2D( + const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate) +{ + if (rTransCandidate.getChildren().empty()) + { + // no content, done + return; + } + + if (0.0 == rTransCandidate.getTransparence()) + { + // not transparent at all, use content + process(rTransCandidate.getChildren()); + return; + } + + if (rTransCandidate.getTransparence() < 0.0 || rTransCandidate.getTransparence() > 1.0) + { + // invalid transparence, done + return; + } + + // calculate visible range + basegfx::B2DRange aDiscreteVisibleRange; + calculateDiscreteVisibleRange(aDiscreteVisibleRange, + rTransCandidate.getChildren().getB2DRange(getViewInformation2D()), + getViewInformation2D()); + + if (aDiscreteVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + bool bDone(false); + sal::systools::COMReference<ID2D1Layer> pLayer; + const HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer)); + + if (SUCCEEDED(hr) && pLayer) + { + // need to set correct transform for Layer initialization, we work + // in discrete device coordinates + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + getRenderTarget()->PushLayer( + D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0 - rTransCandidate.getTransparence()), // opacity + pLayer); + process(rTransCandidate.getChildren()); + getRenderTarget()->PopLayer(); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate) +{ + if (rMaskCandidate.getChildren().empty()) + { + // no content, done + return; + } + + basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); + + if (!aMask.count()) + { + // no mask (so nothing inside), done + return; + } + + // calculate visible range + basegfx::B2DRange aDiscreteVisibleRange; + calculateDiscreteVisibleRange(aDiscreteVisibleRange, aMask.getB2DRange(), + getViewInformation2D()); + + if (aDiscreteVisibleRange.isEmpty()) + { + // not visible, done + return; + } + + bool bDone(false); + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1MaskGeometry( + getOrCreateFillGeometry(rMaskCandidate.getMask())); + + if (pSystemDependentData_ID2D1MaskGeometry) + { + sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedMaskGeometry; + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry( + pSystemDependentData_ID2D1MaskGeometry->getID2D1PathGeometry(), + D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e(), rObjectToView.f()), + &pTransformedMaskGeometry)); + + if (SUCCEEDED(hr) && pTransformedMaskGeometry) + { + sal::systools::COMReference<ID2D1Layer> pLayer; + hr = getRenderTarget()->CreateLayer(nullptr, &pLayer); + + if (SUCCEEDED(hr) && pLayer) + { + // need to set correct transform for Layer initialization, we work + // in discrete device coordinates + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + getRenderTarget()->PushLayer( + D2D1::LayerParameters(D2D1::InfiniteRect(), pTransformedMaskGeometry), pLayer); + process(rMaskCandidate.getChildren()); + getRenderTarget()->PopLayer(); + bDone = true; + } + } + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processPointArrayPrimitive2D( + const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate) +{ + const std::vector<basegfx::B2DPoint>& rPositions(rPointArrayCandidate.getPositions()); + + if (rPositions.empty()) + { + // no geometry, done + return; + } + + const basegfx::BColor aPointColor( + maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor())); + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + D2D1::ColorF aD2DColor(aPointColor.getRed(), aPointColor.getGreen(), aPointColor.getBlue()); + const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush)); + bool bDone(false); + + if (SUCCEEDED(hr) && pColorBrush) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + + // To really paint a single pixel I found nothing better than + // switch off AA and draw a pixel-aligned rectangle + const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode()); + getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + for (auto const& pos : rPositions) + { + const basegfx::B2DPoint aDiscretePos( + getViewInformation2D().getObjectToViewTransformation() * pos); + const double fX(ceil(aDiscretePos.getX())); + const double fY(ceil(aDiscretePos.getY())); + const D2D1_RECT_F rect = { FLOAT(fX), FLOAT(fY), FLOAT(fX), FLOAT(fY) }; + + getRenderTarget()->DrawRectangle(&rect, pColorBrush); + } + + getRenderTarget()->SetAntialiasMode(aOldAAMode); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processMarkerArrayPrimitive2D( + const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate) +{ + const std::vector<basegfx::B2DPoint>& rPositions(rMarkerArrayCandidate.getPositions()); + + if (rPositions.empty()) + { + // no geometry, done + return; + } + + Bitmap rMarker(rMarkerArrayCandidate.getMarker()); + + if (rMarker.IsEmpty()) + { + // no marker defined, done + return; + } + + sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap( + getOrCreateB2DBitmap(getRenderTarget(), rMarker)); + bool bDone(false); + + if (pD2DBitmap) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + const Size& rSizePixel(rMarker.GetSizePixel()); + const tools::Long nMiX((rSizePixel.Width() / 2) + 1); + const tools::Long nMiY((rSizePixel.Height() / 2) + 1); + const tools::Long nPlX(rSizePixel.Width() - nMiX); + const tools::Long nPlY(rSizePixel.Height() - nMiY); + + // draw with non-AA to show unhampered, clear, non-scaled marker + const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode()); + getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + + for (auto const& pos : rPositions) + { + const basegfx::B2DPoint aDiscretePos( + getViewInformation2D().getObjectToViewTransformation() * pos); + const double fX(ceil(aDiscretePos.getX())); + const double fY(ceil(aDiscretePos.getY())); + const D2D1_RECT_F rect + = { FLOAT(fX - nMiX), FLOAT(fY - nMiY), FLOAT(fX + nPlX), FLOAT(fY + nPlY) }; + + getRenderTarget()->DrawBitmap(pD2DBitmap, &rect); + } + + getRenderTarget()->SetAntialiasMode(aOldAAMode); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processBackgroundColorPrimitive2D( + const primitive2d::BackgroundColorPrimitive2D& rBackgroundColorCandidate) +{ + // check for allowed range [0.0 .. 1.0[ + if (rBackgroundColorCandidate.getTransparency() < 0.0 + || rBackgroundColorCandidate.getTransparency() >= 1.0) + return; + + const D2D1::ColorF aD2DColor(rBackgroundColorCandidate.getBColor().getRed(), + rBackgroundColorCandidate.getBColor().getGreen(), + rBackgroundColorCandidate.getBColor().getBlue(), + 1.0 - rBackgroundColorCandidate.getTransparency()); + + getRenderTarget()->Clear(aD2DColor); +} + +void D2DPixelProcessor2D::processModifiedColorPrimitive2D( + const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate) +{ + if (!rModifiedCandidate.getChildren().empty()) + { + maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); + process(rModifiedCandidate.getChildren()); + maBColorModifierStack.pop(); + } +} + +void D2DPixelProcessor2D::processTransformPrimitive2D( + const primitive2d::TransformPrimitive2D& rTransformCandidate) +{ + // remember current transformation and ViewInformation + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for local ViewInformation2D + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); + + // process content + process(rTransformCandidate.getChildren()); + + // restore transformations + setViewInformation2D(aLastViewInformation2D); +} + +void D2DPixelProcessor2D::processPolygonStrokePrimitive2D( + const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate) +{ + const basegfx::B2DPolygon& rPolygon(rPolygonStrokeCandidate.getB2DPolygon()); + const attribute::LineAttribute& rLineAttribute(rPolygonStrokeCandidate.getLineAttribute()); + + if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0) + { + // no geometry, done + return; + } + + // get some values early that might be used for decisions + const bool bHairline(0.0 == rLineAttribute.getWidth()); + const basegfx::B2DHomMatrix& rObjectToView( + getViewInformation2D().getObjectToViewTransformation()); + const double fDiscreteLineWidth( + bHairline + ? 1.0 + : (rObjectToView * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)).getLength()); + + // Here for every combination which the system-specific implementation is not + // capable of visualizing, use the (for decomposable Primitives always possible) + // fallback to the decomposition. + if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && fDiscreteLineWidth > 1.5) + { + // basegfx::B2DLineJoin::NONE is special for our office, no other GraphicSystem + // knows that (so far), so fallback to decomposition. This is only needed if + // LineJoin will be used, so also check for discrete LineWidth before falling back + process(rPolygonStrokeCandidate); + return; + } + + // This is a method every system-specific implementation of a decomposable Primitive + // can use to allow simple optical control of paint implementation: + // Create a copy, e.g. change color to 'red' as here and paint before the system + // paints it using the decomposition. That way you can - if active - directly + // optically compare if the system-specific solution is geometrically identical to + // the decomposition (which defines our interpretation that we need to visualize). + // Look below in the impl for bRenderDecomposeForCompareInRed to see that in that case + // we create a half-transparent paint to better support visual control + static bool bRenderDecomposeForCompareInRed(false); + + if (bRenderDecomposeForCompareInRed) + { + const attribute::LineAttribute aRed( + basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), rLineAttribute.getLineJoin(), + rLineAttribute.getLineCap(), rLineAttribute.getMiterMinimumAngle()); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> aCopy( + new primitive2d::PolygonStrokePrimitive2D( + rPolygonStrokeCandidate.getB2DPolygon(), aRed, + rPolygonStrokeCandidate.getStrokeAttribute())); + process(*aCopy); + } + + bool bDone(false); + std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry( + getOrCreatePathGeometry(rPolygon, getViewInformation2D())); + + if (pSystemDependentData_ID2D1PathGeometry) + { + sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry; + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry( + pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(), + D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(), + rObjectToView.d(), rObjectToView.e() + fAAOffset, + rObjectToView.f() + fAAOffset), + &pTransformedGeometry)); + + if (SUCCEEDED(hr) && pTransformedGeometry) + { + const basegfx::BColor aLineColor( + maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); + D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(), + aLineColor.getBlue()); + + if (bRenderDecomposeForCompareInRed) + { + aD2DColor.a = 0.5; + } + + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush); + + if (SUCCEEDED(hr) && pColorBrush) + { + sal::systools::COMReference<ID2D1StrokeStyle> pStrokeStyle; + D2D1_CAP_STYLE aCapStyle(D2D1_CAP_STYLE_FLAT); + D2D1_LINE_JOIN aLineJoin(D2D1_LINE_JOIN_MITER); + const attribute::StrokeAttribute& rStrokeAttribute( + rPolygonStrokeCandidate.getStrokeAttribute()); + const bool bDashUsed(!rStrokeAttribute.isDefault() + && !rStrokeAttribute.getDotDashArray().empty() + && 0.0 < rStrokeAttribute.getFullDotDashLen()); + D2D1_DASH_STYLE aDashStyle(bDashUsed ? D2D1_DASH_STYLE_CUSTOM + : D2D1_DASH_STYLE_SOLID); + std::vector<float> dashes; + float miterLimit(1.0); + + switch (rLineAttribute.getLineCap()) + { + case css::drawing::LineCap_ROUND: + aCapStyle = D2D1_CAP_STYLE_ROUND; + break; + case css::drawing::LineCap_SQUARE: + aCapStyle = D2D1_CAP_STYLE_SQUARE; + break; + default: + break; + } + + switch (rLineAttribute.getLineJoin()) + { + case basegfx::B2DLineJoin::NONE: + break; + case basegfx::B2DLineJoin::Bevel: + aLineJoin = D2D1_LINE_JOIN_BEVEL; + break; + case basegfx::B2DLineJoin::Miter: + { + // for basegfx::B2DLineJoin::Miter there are two problems: + // (1) MS uses D2D1_LINE_JOIN_MITER which handles the cut-off when MiterLimit is hit not by + // fallback to Bevel, but by cutting miter geometry at the defined distance. That is + // nice, but not what we need or is the standard for other graphic systems. Luckily there + // is also D2D1_LINE_JOIN_MITER_OR_BEVEL and (after some search) the page + // https://learn.microsoft.com/en-us/windows/win32/api/d2d1/ne-d2d1-d2d1_line_join + // which gives some explanation, so that is what we need to use here. + // (2) Instead of using an angle in radians (15 deg default) MS uses + // "miterLimit is relative to 1/2 LineWidth", so a length. After some experimenting + // it shows that the (better understandable) angle has to be converted to the length + // that a miter prolongation would have at that angle, so use some trigonometry. + // Unfortunately there is also some'precision' problem (probably), so I had to + // experimentally come to a correction value around 0.9925. Since that seems to + // be no obvious numerical value involved somehow (and as long as I find no other + // explanation) I will have to use that. + // NOTE: To find that correction value I usd that handy bRenderDecomposeForCompareInRed + // and changes in debugger - as work tipp + // With both done I can use Direct2D for Miter completely - what is good for speed. + aLineJoin = D2D1_LINE_JOIN_MITER_OR_BEVEL; + + // snap absolute value of angle in radians to [0.0 .. PI] + double fVal(::basegfx::snapToZeroRange( + fabs(rLineAttribute.getMiterMinimumAngle()), M_PI)); + + // cut at 0.0 and PI since sin would be zero ('endless' miter) + const double fSmallValue(M_PI * 0.0000001); + fVal = std::max(fSmallValue, fVal); + fVal = std::min(M_PI - fSmallValue, fVal); + + // get relative length + fVal = 1.0 / sin(fVal); + + // use for miterLimit, we need factor 2.0 (relative to double LineWidth) + // and the correction mentioned in (2) above + const double fCorrector(2.0 * 0.9925); + + miterLimit = fVal * fCorrector; + break; + } + case basegfx::B2DLineJoin::Round: + aLineJoin = D2D1_LINE_JOIN_ROUND; + break; + default: + break; + } + + if (bDashUsed) + { + // dashes need to be discrete and relative to LineWidth + for (auto& value : rStrokeAttribute.getDotDashArray()) + { + dashes.push_back( + (rObjectToView * basegfx::B2DVector(value, 0.0)).getLength() + / fDiscreteLineWidth); + } + } + + hr = aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateStrokeStyle( + D2D1::StrokeStyleProperties(aCapStyle, // startCap + aCapStyle, // endCap + aCapStyle, // dashCap + aLineJoin, // lineJoin + miterLimit, // miterLimit + aDashStyle, // dashStyle + 0.0f), // dashOffset + bDashUsed ? dashes.data() : nullptr, bDashUsed ? dashes.size() : 0, + &pStrokeStyle); + + if (SUCCEEDED(hr) && pStrokeStyle) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + getRenderTarget()->DrawGeometry( + pTransformedGeometry, pColorBrush, + // TODO: Hairline LineWidth, see comment at processPolygonHairlinePrimitive2D + bHairline ? 1.44 : fDiscreteLineWidth, pStrokeStyle); + bDone = true; + } + } + } + } + + if (!bDone) + { + // fallback to decomposition + process(rPolygonStrokeCandidate); + } +} + +void D2DPixelProcessor2D::processLineRectanglePrimitive2D( + const primitive2d::LineRectanglePrimitive2D& rLineRectanglePrimitive2D) +{ + if (rLineRectanglePrimitive2D.getB2DRange().isEmpty()) + { + // no geometry, done + return; + } + + const basegfx::BColor aHairlineColor( + maBColorModifierStack.getModifiedColor(rLineRectanglePrimitive2D.getBColor())); + const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(), + aHairlineColor.getBlue()); + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush)); + bool bDone(false); + + if (SUCCEEDED(hr) && pColorBrush) + { + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation()); + getRenderTarget()->SetTransform(D2D1::Matrix3x2F( + aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(), + aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset)); + const basegfx::B2DRange& rRange(rLineRectanglePrimitive2D.getB2DRange()); + const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()), + FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) }; + const double fDiscreteLineWidth( + (getViewInformation2D().getInverseObjectToViewTransformation() + * basegfx::B2DVector(1.44, 0.0)) + .getLength()); + + getRenderTarget()->DrawRectangle(&rect, pColorBrush, fDiscreteLineWidth); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processFilledRectanglePrimitive2D( + const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D) +{ + if (rFilledRectanglePrimitive2D.getB2DRange().isEmpty()) + { + // no geometry, done + return; + } + + const basegfx::BColor aFillColor( + maBColorModifierStack.getModifiedColor(rFilledRectanglePrimitive2D.getBColor())); + const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue()); + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush)); + bool bDone(false); + + if (SUCCEEDED(hr) && pColorBrush) + { + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation()); + getRenderTarget()->SetTransform(D2D1::Matrix3x2F( + aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(), + aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset)); + const basegfx::B2DRange& rRange(rFilledRectanglePrimitive2D.getB2DRange()); + const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()), + FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) }; + + getRenderTarget()->FillRectangle(&rect, pColorBrush); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processSingleLinePrimitive2D( + const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D) +{ + const basegfx::BColor aLineColor( + maBColorModifierStack.getModifiedColor(rSingleLinePrimitive2D.getBColor())); + const D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue()); + sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush; + const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush)); + bool bDone(false); + + if (SUCCEEDED(hr) && pColorBrush) + { + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation()); + const basegfx::B2DPoint aStart(aLocalTransform * rSingleLinePrimitive2D.getStart()); + const basegfx::B2DPoint aEnd(aLocalTransform * rSingleLinePrimitive2D.getEnd()); + + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + const D2D1_POINT_2F aD2D1Start + = { FLOAT(aStart.getX() + fAAOffset), FLOAT(aStart.getY() + fAAOffset) }; + const D2D1_POINT_2F aD2D1End + = { FLOAT(aEnd.getX() + fAAOffset), FLOAT(aEnd.getY() + fAAOffset) }; + + getRenderTarget()->DrawLine(aD2D1Start, aD2D1End, pColorBrush, 1.44f); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processFillGraphicPrimitive2D( + const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D) +{ + if (rFillGraphicPrimitive2D.getTransparency() < 0.0 + || rFillGraphicPrimitive2D.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + if (rFillGraphicPrimitive2D.hasTransparency()) + { + // cannot handle yet, use decomposition + process(rFillGraphicPrimitive2D); + return; + } + + Bitmap aPreparedBitmap; + basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange()); + constexpr double fBigDiscreteArea(300.0 * 300.0); + + // use tooling to do various checks and prepare tiled rendering, see + // description of method, parameters and return value there + if (!prepareBitmapForDirectRender(rFillGraphicPrimitive2D, getViewInformation2D(), + aPreparedBitmap, aFillUnitRange, fBigDiscreteArea)) + { + // no output needed, done + return; + } + + if (aPreparedBitmap.IsEmpty()) + { + // output needed and Bitmap data empty, so no bitmap data based + // tiled rendering is suggested. Use fallback for paint (decomposition) + process(rFillGraphicPrimitive2D); + return; + } + + // render tiled using the prepared Bitmap data + if (maBColorModifierStack.count()) + { + // need to apply ColorModifier to Bitmap data + aPreparedBitmap = aPreparedBitmap.Modify(maBColorModifierStack); + + if (aPreparedBitmap.IsEmpty()) + { + // color gets completely replaced, get it (any input works) + const basegfx::BColor aModifiedColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + + // use unit geometry as fallback object geometry. Do *not* + // transform, the below used method will use the already + // correctly initialized local ViewInformation + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + + // what we still need to apply is the object transform from the + // local primitive, that is not part of DisplayInfo yet + aPolygon.transform(rFillGraphicPrimitive2D.getTransformation()); + + rtl::Reference<primitive2d::PolyPolygonColorPrimitive2D> aTemp( + new primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPolygon), + aModifiedColor)); + + // draw as colored Polygon, done + processPolyPolygonColorPrimitive2D(*aTemp); + return; + } + } + + bool bDone(false); + sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap( + getOrCreateB2DBitmap(getRenderTarget(), aPreparedBitmap)); + + if (pD2DBitmap) + { + sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush; + const HRESULT hr(getRenderTarget()->CreateBitmapBrush(pD2DBitmap, &pBitmapBrush)); + + if (SUCCEEDED(hr) && pBitmapBrush) + { + // set extended to repeat/wrap AKA tiling + pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_WRAP); + pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_WRAP); + + // set interpolation mode + // NOTE: This uses D2D1_BITMAP_INTERPOLATION_MODE, but there seem to be + // advanced modes when using D2D1_INTERPOLATION_MODE, but that needs + // D2D1_BITMAP_BRUSH_PROPERTIES1 and ID2D1BitmapBrush1 + sal::systools::COMReference<ID2D1BitmapBrush1> pBrush1; + pBitmapBrush->QueryInterface(__uuidof(ID2D1BitmapBrush1), + reinterpret_cast<void**>(&pBrush1)); + + if (pBrush1) + { + pBrush1->SetInterpolationMode1(D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR); + } + else + { + pBitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR); + } + + // set BitmapBrush transformation relative to it's PixelSize and + // the used FillUnitRange. Since we use unit coordinates here this + // is pretty simple + const D2D1_SIZE_U aBMSPixel(pD2DBitmap->GetPixelSize()); + const double fScaleX((aFillUnitRange.getMaxX() - aFillUnitRange.getMinX()) + / aBMSPixel.width); + const double fScaleY((aFillUnitRange.getMaxY() - aFillUnitRange.getMinY()) + / aBMSPixel.height); + const D2D1_MATRIX_3X2_F aBTrans(D2D1::Matrix3x2F( + fScaleX, 0.0, 0.0, fScaleY, aFillUnitRange.getMinX(), aFillUnitRange.getMinY())); + pBitmapBrush->SetTransform(&aBTrans); + + // set transform to ObjectToWorld to be able to paint in unit coordinates, so + // evtl. shear/rotate in that transform is used and does not influence the + // orthogonal and unit-oriented brush handling + const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0); + const basegfx::B2DHomMatrix aLocalTransform( + getViewInformation2D().getObjectToViewTransformation() + * rFillGraphicPrimitive2D.getTransformation()); + getRenderTarget()->SetTransform(D2D1::Matrix3x2F( + aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(), + aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset)); + + // use unit rectangle, transformation is already set to include ObjectToWorld + const D2D1_RECT_F rect = { FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(1.0) }; + + // draw as unit rectangle as brush filled rectangle + getRenderTarget()->FillRectangle(&rect, pBitmapBrush); + bDone = true; + } + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processFillGradientPrimitive2D( + const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D) +{ + if (rFillGradientPrimitive2D.hasAlphaGradient() || rFillGradientPrimitive2D.hasTransparency()) + { + // SDPR: As long as direct alpha is not supported by this + // renderer we need to work on the decomposition, so call it + process(rFillGradientPrimitive2D); + return; + } + + // draw all-covering initial BG polygon 1st + bool bDone(drawPolyPolygonColorTransformed( + basegfx::B2DHomMatrix(), + basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect(rFillGradientPrimitive2D.getOutputRange())), + rFillGradientPrimitive2D.getOuterColor())); + + if (bDone) + { + const basegfx::B2DPolyPolygon aForm(rFillGradientPrimitive2D.getUnitPolygon()); + + // paint solid fill steps by providing callback as lambda + auto aCallback([&aForm, &bDone, this](const basegfx::B2DHomMatrix& rMatrix, + const basegfx::BColor& rColor) { + if (bDone) + { + bDone = drawPolyPolygonColorTransformed(rMatrix, aForm, rColor); + } + }); + + // call value generator to trigger callbacks + rFillGradientPrimitive2D.generateMatricesAndColors(aCallback); + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processInvertPrimitive2D( + const primitive2d::InvertPrimitive2D& rInvertPrimitive2D) +{ + if (rInvertPrimitive2D.getChildren().empty()) + { + // no content, done + return; + } + + // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface. + // Only with ID2D1DeviceContext we can use ::DrawImage which supports + // D2D1_COMPOSITE_MODE_XOR + sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext; + getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext), + reinterpret_cast<void**>(&pID2D1DeviceContext)); + + if (!pID2D1DeviceContext) + { + // TODO: We have *no* ID2D1DeviceContext and cannot use D2D1_COMPOSITE_MODE_XOR, + // so there is currently no (simple?) way to solve this, there is no 'Invert' method. + // It may be possible to convert to a WICBitmap (gets read access) and do the invert + // there, but that needs experimenting and is probably not performant - but doable. + increaseError(); + return; + } + + sal::systools::COMReference<ID2D1Bitmap> pInBetweenResult; + basegfx::B2DRange aDiscreteVisibleRange; + + // create in-between result in discrete coordinates, clipped against visible + // part of ViewInformation (if available) + if (!createBitmapSubContent(pInBetweenResult, aDiscreteVisibleRange, + rInvertPrimitive2D.getChildren(), getViewInformation2D(), + getRenderTarget())) + { + // return of false means no display needed, return + return; + } + + bool bDone(false); + + if (pInBetweenResult) + { + getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity()); + const D2D1_POINT_2F aTopLeft = { FLOAT(floor(aDiscreteVisibleRange.getMinX())), + FLOAT(floor(aDiscreteVisibleRange.getMinY())) }; + + pID2D1DeviceContext->DrawImage(pInBetweenResult, aTopLeft, D2D1_INTERPOLATION_MODE_LINEAR, + D2D1_COMPOSITE_MODE_XOR); + bDone = true; + } + + if (!bDone) + increaseError(); +} + +void D2DPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + if (0 == mnRecursionCounter) + getRenderTarget()->BeginDraw(); + mnRecursionCounter++; + + switch (rCandidate.getPrimitive2DID()) + { + // Geometry that *has* to be processed + // + // These Primitives have *no* decompose implementation, so these are the basic ones + // Just four to go to make a processor work completely (but not optimized) + // NOTE: This *could* theoretically be reduced to one and all could implement + // a decompose to pixel data, but that seemed not to make sense to me when + // I designed this. Thus these four are the lowest-level best representation + // from my POV + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: + { + processBitmapPrimitive2D( + static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: + { + processPointArrayPrimitive2D( + static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: + { + processPolygonHairlinePrimitive2D( + static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: + { + processPolyPolygonColorPrimitive2D( + static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); + break; + } + + // Embedding/groups that *have* to be processed + // + // These represent qualifiers for freely defined content, e.g. making + // any combination of primitives freely transformed or transparent + // NOTE: PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D and + // PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D are pretty much default- + // implementations that can and are re-used in all processors. + // So - with these and PRIMITIVE2D_ID_INVERTPRIMITIVE2D marked to + // be removed in the future - just three really to be implemented + // locally specifically + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: + { + processTransparencePrimitive2D( + static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: + { + // We urgently should get rid of XOR paint, modern graphic systems + // allow no read access to the pixel targets, but that's naturally + // a precondition for XOR. While we can do that for the office's + // visualization, we can in principle *not* fully avoid getting + // stuff that needs/defines XOR paint, e.g. EMF/WMF imports, so + // we *have* to support it (for now - sigh)... + processInvertPrimitive2D( + static_cast<const primitive2d::InvertPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MASKPRIMITIVE2D: + { + processMaskPrimitive2D(static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: + { + processModifiedColorPrimitive2D( + static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: + { + processTransformPrimitive2D( + static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); + break; + } + + // Geometry that *may* be processed due to being able to do it better + // then using the decomposition. + // NOTE: In these implementations you could always call what the default + // case below does - call process(rCandidate) to use the decomposition. + // So these impls should only do something if they can do it better/ + // faster that the decomposition. So some of them check if they could + // - and if not - use exactly that. + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: + { + // transparence with a fixed alpha for all content, can be done + // significally faster + processUnifiedTransparencePrimitive2D( + static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: + { + // can be done simpler and without AA better locally + processMarkerArrayPrimitive2D( + static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: + { + // reset to a color, can be done more effectively locally, would + // else decompose to a polygon fill + processBackgroundColorPrimitive2D( + static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + // fat and stroked lines - much better doable locally, would decompose + // to the full line geometry creation (tessellation) + processPolygonStrokePrimitive2D( + static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D: + { + // simple primitive to support future fast callbacks from OutputDevice + // (see 'Example POC' in Gerrit), decomposes to polygon primitive + processLineRectanglePrimitive2D( + static_cast<const primitive2d::LineRectanglePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D: + { + // simple primitive to support future fast callbacks from OutputDevice + // (see 'Example POC' in Gerrit), decomposes to filled polygon primitive + processFilledRectanglePrimitive2D( + static_cast<const primitive2d::FilledRectanglePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D: + { + // simple primitive to support future fast callbacks from OutputDevice + // (see 'Example POC' in Gerrit), decomposes to polygon primitive + processSingleLinePrimitive2D( + static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: + { + processFillGraphicPrimitive2D( + static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D: + { + processFillGradientPrimitive2D( + static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate)); + break; + } + + // continue with decompose as fallback + default: + { + SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString( + rCandidate.getPrimitive2DID())); + // process recursively + process(rCandidate); + break; + } + } + + mnRecursionCounter--; + if (0 == mnRecursionCounter) + getRenderTarget()->EndDraw(); +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/getdigitlanguage.cxx b/drawinglayer/source/processor2d/getdigitlanguage.cxx index 858284b23e91..464fbf642af4 100644 --- a/drawinglayer/source/processor2d/getdigitlanguage.cxx +++ b/drawinglayer/source/processor2d/getdigitlanguage.cxx @@ -18,7 +18,7 @@ #include "getdigitlanguage.hxx" LanguageType drawinglayer::detail::getDigitLanguage() { - switch (SvtCTLOptions().GetCTLTextNumerals()) { + switch (SvtCTLOptions::GetCTLTextNumerals()) { case SvtCTLOptions::NUMERALS_ARABIC: return LANGUAGE_ENGLISH; case SvtCTLOptions::NUMERALS_HINDI: diff --git a/drawinglayer/source/processor2d/getdigitlanguage.hxx b/drawinglayer/source/processor2d/getdigitlanguage.hxx index c634321b36f9..c22076fa7cd0 100644 --- a/drawinglayer/source/processor2d/getdigitlanguage.hxx +++ b/drawinglayer/source/processor2d/getdigitlanguage.hxx @@ -7,20 +7,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_PROCESSOR2D_GETDIGITLANGUAGE_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_PROCESSOR2D_GETDIGITLANGUAGE_HXX +#pragma once #include <sal/config.h> #include <i18nlangtag/lang.h> -namespace drawinglayer::detail { - +namespace drawinglayer::detail +{ /// Get digit language derived from SvtCTLOptions LanguageType getDigitLanguage(); - } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx b/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx deleted file mode 100644 index 9f838a7e1b61..000000000000 --- a/drawinglayer/source/processor2d/helperwrongspellrenderer.cxx +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- 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 "helperwrongspellrenderer.hxx" -#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> -#include <tools/gen.hxx> -#include <vcl/outdev.hxx> -#include <basegfx/color/bcolormodifier.hxx> -#include <vcl/outdev/ScopedStates.hxx> - -using namespace css; - -namespace drawinglayer -{ -namespace -{ -constexpr sal_uInt32 constMinimumFontHeight = 5; // #define WRONG_SHOW_MIN 5 -} - -bool renderWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellCandidate, - OutputDevice& rOutputDevice, - const basegfx::B2DHomMatrix& rObjectToViewTransformation, - const basegfx::BColorModifierStack& rBColorModifierStack) -{ - const basegfx::B2DHomMatrix aLocalTransform(rObjectToViewTransformation - * rWrongSpellCandidate.getTransformation()); - const basegfx::B2DVector aFontVectorPixel(aLocalTransform * basegfx::B2DVector(0.0, 1.0)); - const sal_uInt32 nFontPixelHeight(basegfx::fround(aFontVectorPixel.getLength())); - - if (nFontPixelHeight <= constMinimumFontHeight) - return true; - - const basegfx::B2DPoint aStart(aLocalTransform - * basegfx::B2DPoint(rWrongSpellCandidate.getStart(), 0.0)); - const basegfx::B2DPoint aStop(aLocalTransform - * basegfx::B2DPoint(rWrongSpellCandidate.getStop(), 0.0)); - const Point aVclStart(basegfx::fround(aStart.getX()), basegfx::fround(aStart.getY())); - const Point aVclStop(basegfx::fround(aStop.getX()), basegfx::fround(aStop.getY())); - - // #i101075# draw it. Do not forget to use the evtl. offsetted origin of the target device, - // e.g. when used with mask/transparence buffer device - const Point aOrigin(rOutputDevice.GetMapMode().GetOrigin()); - - const basegfx::BColor aProcessedColor( - rBColorModifierStack.getModifiedColor(rWrongSpellCandidate.getColor())); - const bool bMapModeEnabledState(rOutputDevice.IsMapModeEnabled()); - - vcl::ScopedAntialiasing a(rOutputDevice, true); - rOutputDevice.EnableMapMode(false); - rOutputDevice.SetLineColor(Color(aProcessedColor)); - rOutputDevice.SetFillColor(); - rOutputDevice.DrawWaveLine(aOrigin + aVclStart, aOrigin + aVclStop); - rOutputDevice.EnableMapMode(bMapModeEnabledState); - - // cannot really go wrong - return true; -} -} // end of namespace drawinglayer - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx index 65a03548cb1c..7f9391298ecb 100644 --- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx +++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx @@ -20,8 +20,11 @@ #include <drawinglayer/processor2d/hittestprocessor2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonWavePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> @@ -39,29 +42,25 @@ namespace drawinglayer::processor2d { HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation, const basegfx::B2DPoint& rLogicHitPosition, - double fLogicHitTolerance, + const basegfx::B2DVector& rLogicHitTolerancePerAxis, bool bHitTextOnly) : BaseProcessor2D(rViewInformation), - maDiscreteHitPosition(), - mfDiscreteHitTolerance(0.0), - maHitStack(), + maDiscreteHitTolerancePerAxis(rLogicHitTolerancePerAxis), mbCollectHitStack(false), mbHit(false), mbHitTextOnly(bHitTextOnly) { - // init hit tolerance - mfDiscreteHitTolerance = fLogicHitTolerance; + // ensure input parameters for hit tolerance is >= 0.0 + if (maDiscreteHitTolerancePerAxis.getX() < 0.0) + maDiscreteHitTolerancePerAxis.setX(0.0); + if (maDiscreteHitTolerancePerAxis.getY() < 0.0) + maDiscreteHitTolerancePerAxis.setY(0.0); - if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0)) - { - // ensure input parameter for hit tolerance is >= 0.0 - mfDiscreteHitTolerance = 0.0; - } - else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0)) + if (!maDiscreteHitTolerancePerAxis.equalZero()) { // generate discrete hit tolerance - mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation() - * basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength(); + maDiscreteHitTolerancePerAxis + = getViewInformation2D().getObjectToViewTransformation() * rLogicHitTolerancePerAxis; } // generate discrete hit position @@ -74,7 +73,7 @@ namespace drawinglayer::processor2d bool HitTestProcessor2D::checkHairlineHitWithTolerance( const basegfx::B2DPolygon& rPolygon, - double fDiscreteHitTolerance) const + const basegfx::B2DVector& rDiscreteHitTolerancePerAxis) const { basegfx::B2DPolygon aLocalPolygon(rPolygon); aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation()); @@ -82,9 +81,9 @@ namespace drawinglayer::processor2d // get discrete range basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange()); - if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0)) + if(rDiscreteHitTolerancePerAxis.getX() > 0 || rDiscreteHitTolerancePerAxis.getY() > 0) { - aPolygonRange.grow(fDiscreteHitTolerance); + aPolygonRange.grow(rDiscreteHitTolerancePerAxis); } // do rough range test first @@ -94,7 +93,7 @@ namespace drawinglayer::processor2d return basegfx::utils::isInEpsilonRange( aLocalPolygon, getDiscreteHitPosition(), - fDiscreteHitTolerance); + std::max(rDiscreteHitTolerancePerAxis.getX(), rDiscreteHitTolerancePerAxis.getY())); } return false; @@ -102,7 +101,7 @@ namespace drawinglayer::processor2d bool HitTestProcessor2D::checkFillHitWithTolerance( const basegfx::B2DPolyPolygon& rPolyPolygon, - double fDiscreteHitTolerance) const + const basegfx::B2DVector& rDiscreteHitTolerancePerAxis) const { bool bRetval(false); basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon); @@ -110,11 +109,13 @@ namespace drawinglayer::processor2d // get discrete range basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange()); - const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0)); - if(bDiscreteHitToleranceUsed) + const bool bDiscreteHitToleranceUsed(rDiscreteHitTolerancePerAxis.getX() > 0 + || rDiscreteHitTolerancePerAxis.getY() > 0); + + if (bDiscreteHitToleranceUsed) { - aPolygonRange.grow(fDiscreteHitTolerance); + aPolygonRange.grow(rDiscreteHitTolerancePerAxis); } // do rough range test first @@ -125,7 +126,7 @@ namespace drawinglayer::processor2d basegfx::utils::isInEpsilonRange( aLocalPolyPolygon, getDiscreteHitPosition(), - fDiscreteHitTolerance)) + std::max(rDiscreteHitTolerancePerAxis.getX(), rDiscreteHitTolerancePerAxis.getY()))) { bRetval = true; } @@ -143,6 +144,54 @@ namespace drawinglayer::processor2d return bRetval; } + void HitTestProcessor2D::checkBitmapHit(basegfx::B2DRange aRange, const Bitmap& rBitmap, const basegfx::B2DHomMatrix& rTransform) + { + if(!getHitTextOnly()) + { + // The recently added Bitmap::GetTransparency() makes it easy to extend + // the BitmapPrimitive2D HitTest to take the contained Bitmap and it's + // transparency into account + if(!aRange.isEmpty()) + { + const Size aSizePixel(rBitmap.GetSizePixel()); + + // When tiled rendering, don't bother with the pixel size of the candidate. + if(aSizePixel.Width() && aSizePixel.Height() && !comphelper::LibreOfficeKit::isActive()) + { + basegfx::B2DHomMatrix aBackTransform( + getViewInformation2D().getObjectToViewTransformation() * + rTransform); + aBackTransform.invert(); + + const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition()); + const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); + + if(aUnitRange.isInside(aRelativePoint)) + { + // aRelativePoint.getX() == 0.0 -> 0 + // aRelativePoint.getX() == 0.999... -> aSizePixel.Width() - 1 + // Since isInside includes upper bound (1.0), force also this: + // aRelativePoint.getX() == 1.0 -> aSizePixel.Width() - 1 + sal_Int32 nX(aRelativePoint.getX() * aSizePixel.Width()); + if (nX == aSizePixel.Width()) + --nX; + sal_Int32 nY(aRelativePoint.getY() * aSizePixel.Height()); + if (nY == aSizePixel.Height()) + --nY; + + mbHit = (0 != rBitmap.GetPixelColor(nX, nY).GetAlpha()); + } + } + else + { + // fallback to standard HitTest + const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); + mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); + } + } + } + } + void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate) { // calculate relative point in unified 2D scene @@ -233,20 +282,15 @@ namespace drawinglayer::processor2d const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // create new local ViewInformation2D containing transformation - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), - getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), - getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process child content recursively process(rTransformCandidate.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); break; } @@ -284,7 +328,7 @@ namespace drawinglayer::processor2d const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate)); const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute(); - if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0)) + if(rLineAttribute.getWidth() > 0.0) { if(basegfx::B2DLineJoin::Miter == rLineAttribute.getLineJoin()) { @@ -296,10 +340,10 @@ namespace drawinglayer::processor2d { // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation() - * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0)); + * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, rLineAttribute.getWidth() * 0.5)); mbHit = checkHairlineHitWithTolerance( rPolygonCandidate.getB2DPolygon(), - getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength()); + getDiscreteHitTolerance() + aDiscreteHalfLineVector); } } else @@ -321,23 +365,23 @@ namespace drawinglayer::processor2d double fLogicHitTolerance(0.0); // if WaveHeight, grow by it - if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0)) + if(rPolygonCandidate.getWaveHeight() > 0.0) { fLogicHitTolerance += rPolygonCandidate.getWaveHeight(); } // if line width, grow by it - if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0)) + if(rPolygonCandidate.getLineAttribute().getWidth() > 0.0) { fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5; } const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation() - * basegfx::B2DVector(fLogicHitTolerance, 0.0)); + * basegfx::B2DVector(fLogicHitTolerance, fLogicHitTolerance)); mbHit = checkHairlineHitWithTolerance( rPolygonCandidate.getB2DPolygon(), - getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength()); + getDiscreteHitTolerance() + aDiscreteHalfLineVector); } break; @@ -421,49 +465,28 @@ namespace drawinglayer::processor2d break; } - case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + case PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D : { - if(!getHitTextOnly()) - { - // The recently added BitmapEx::GetTransparency() makes it easy to extend - // the BitmapPrimitive2D HitTest to take the contained BitmapEx and it's - // transparency into account - const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - - if(!aRange.isEmpty()) - { - const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); - const BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap())); - const Size& rSizePixel(aBitmapEx.GetSizePixel()); + // avoid decompose of this primitive by handling directly + const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaCandidate(static_cast< const primitive2d::BitmapAlphaPrimitive2D& >(rCandidate)); - // When tiled rendering, don't bother with the pixel size of the candidate. - if(rSizePixel.Width() && rSizePixel.Height() && !comphelper::LibreOfficeKit::isActive()) - { - basegfx::B2DHomMatrix aBackTransform( - getViewInformation2D().getObjectToViewTransformation() * - rBitmapCandidate.getTransform()); - aBackTransform.invert(); - - const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition()); - const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); - - if(aUnitRange.isInside(aRelativePoint)) - { - const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width())); - const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height())); - - mbHit = (0xff != aBitmapEx.GetTransparency(nX, nY)); - } - } - else - { - // fallback to standard HitTest - const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); - mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance()); - } - } + if (!basegfx::fTools::equal(rBitmapAlphaCandidate.getTransparency(), 1.0)) + { + checkBitmapHit( + rCandidate.getB2DRange(getViewInformation2D()), + rBitmapAlphaCandidate.getBitmap(), + rBitmapAlphaCandidate.getTransform()); } - + break; + } + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : + { + // use common tooling + const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate)); + checkBitmapHit( + rCandidate.getB2DRange(getViewInformation2D()), + rBitmapCandidate.getBitmap(), + rBitmapCandidate.getTransform()); break; } case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : @@ -473,6 +496,8 @@ namespace drawinglayer::processor2d case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D : case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D: + case PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D: + case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D: { if(!getHitTextOnly()) { @@ -521,7 +546,8 @@ namespace drawinglayer::processor2d const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]); const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition()); - if(aDistance.getLength() <= getDiscreteHitTolerance()) + if (aDistance.getLength() <= std::max(getDiscreteHitTolerance().getX(), + getDiscreteHitTolerance().getY())) { mbHit = true; } @@ -543,7 +569,7 @@ namespace drawinglayer::processor2d { /// push candidate to HitStack to create it. This only happens when a hit is found and /// creating the HitStack was requested (see collectHitStack) - maHitStack.append(primitive2d::Primitive2DReference(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate))); + maHitStack.append(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate)); } } diff --git a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx index 9262e23e4509..8c07c8a17c0a 100644 --- a/drawinglayer/source/processor2d/linegeometryextractor2d.cxx +++ b/drawinglayer/source/processor2d/linegeometryextractor2d.cxx @@ -19,20 +19,15 @@ #include <drawinglayer/processor2d/linegeometryextractor2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> -using namespace com::sun::star; - - namespace drawinglayer::processor2d { LineGeometryExtractor2D::LineGeometryExtractor2D(const geometry::ViewInformation2D& rViewInformation) : BaseProcessor2D(rViewInformation), - maExtractedHairlines(), - maExtractedLineFills(), mbInLineGeometry(false) { } @@ -86,20 +81,15 @@ namespace drawinglayer::processor2d const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // create new transformations for CurrentTransformation and for local ViewInformation2D - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), - getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), - getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process content process(rTransformCandidate.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); break; } diff --git a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx index 552406d53f68..d1162f6c2af3 100644 --- a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx +++ b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx @@ -21,8 +21,6 @@ #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> -using namespace com::sun::star; - namespace drawinglayer::processor2d { void ObjectInfoPrimitiveExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx b/drawinglayer/source/processor2d/processor2dtools.cxx index 7bc0f5fa0536..366e2ed469f7 100644 --- a/drawinglayer/source/processor2d/processor2dtools.cxx +++ b/drawinglayer/source/processor2d/processor2dtools.cxx @@ -20,43 +20,165 @@ #include <vcl/gdimtf.hxx> #include "vclpixelprocessor2d.hxx" #include "vclmetafileprocessor2d.hxx" +#include <config_vclplug.h> +#if defined(_WIN32) +#include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx> +#include <vcl/sysdata.hxx> +#elif USE_HEADLESS_CODE +#include <drawinglayer/processor2d/cairopixelprocessor2d.hxx> +#include <officecfg/Office/Common.hxx> +#endif using namespace com::sun::star; - namespace drawinglayer::processor2d { - std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice( - OutputDevice& rTargetOutDev, - const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) +std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromScratch( + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, + sal_uInt32 nPixelWidth, + sal_uInt32 nPixelHeight, + bool bUseRGBA) +{ + if (0 == nPixelWidth || 0 == nPixelHeight) + // error: no size given + return nullptr; + +#if USE_HEADLESS_CODE + // Linux/Cairo: now globally activated in master. Leave a + // possibility to deactivate for easy test/request testing + static bool bUsePrimitiveRenderer(nullptr == std::getenv("DISABLE_SYSTEM_DEPENDENT_PRIMITIVE_RENDERER")); + + if (bUsePrimitiveRenderer) + { + // create CairoPixelProcessor2D with given size + std::unique_ptr<CairoPixelProcessor2D> aRetval( + std::make_unique<CairoPixelProcessor2D>( + rViewInformation2D, + nPixelWidth, + nPixelHeight, + bUseRGBA)); + + if (aRetval->valid()) + return aRetval; + } +#endif + + // avoid unused parameter errors + (void)rViewInformation2D; + (void)nPixelWidth; + (void)nPixelHeight; + (void)bUseRGBA; + + // error: no result when no SDPR supported + return nullptr; +} + +std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice( + OutputDevice& rTargetOutDev, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) +{ +#if defined(_WIN32) + // Windows: make dependent on TEST_SYSTEM_PRIMITIVE_RENDERER + static bool bUsePrimitiveRenderer(nullptr != std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER")); + + if (bUsePrimitiveRenderer) + { + drawinglayer::geometry::ViewInformation2D aViewInformation2D(rViewInformation2D); + + // if mnOutOffX/mnOutOffY is set (a 'hack' to get a cheap additional offset), apply it additionally + // NOTE: This will also need to take extended size of target device into + // consideration, using D2DPixelProcessor2D *will* have to clip + // against that. Thus for now this is *not* sufficient (see tdf#163125) + if(0 != rTargetOutDev.GetOutOffXPixel() || 0 != rTargetOutDev.GetOutOffYPixel()) { - // create Pixel Vcl-Processor - return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev); + basegfx::B2DHomMatrix aTransform(aViewInformation2D.getViewTransformation()); + aTransform.translate(rTargetOutDev.GetOutOffXPixel(), rTargetOutDev.GetOutOffYPixel()); + aViewInformation2D.setViewTransformation(aTransform); } - std::unique_ptr<BaseProcessor2D> createProcessor2DFromOutputDevice( - OutputDevice& rTargetOutDev, - const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) + SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData()); + std::unique_ptr<D2DPixelProcessor2D> aRetval( + std::make_unique<D2DPixelProcessor2D>(aViewInformation2D, aData.hDC)); + if (aRetval->valid()) + return aRetval; + } +#elif USE_HEADLESS_CODE + // Linux/Cairo: now globally activated in master. Leave a + // possibility to deactivate for easy test/request testing + static bool bUsePrimitiveRenderer(nullptr == std::getenv("DISABLE_SYSTEM_DEPENDENT_PRIMITIVE_RENDERER")); + + if (bUsePrimitiveRenderer) + { + // tdf#165061 do not use SDPR when RTL is enabled, SDPR is designed + // for rendering EditViews and does not support RTL (yet?) + // tdf#165437 also need to check for HasMirroredGraphics to + // get *all* mirrorings covered + const bool bMirrored(rTargetOutDev.IsRTLEnabled() || rTargetOutDev.HasMirroredGraphics()); + + if (!bMirrored) { - const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile(); - const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); + // create CairoPixelProcessor2D associated with the given + // OutputDevice + std::unique_ptr<CairoPixelProcessor2D> aRetval( + std::make_unique<CairoPixelProcessor2D>( + rTargetOutDev, + rViewInformation2D)); - if(bOutputToRecordingMetaFile) + if (aRetval->valid()) { - // create MetaFile Vcl-Processor and process - return std::make_unique<VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev); - } - else - { - // create Pixel Vcl-Processor - return createPixelProcessor2DFromOutputDevice( - rTargetOutDev, - rViewInformation2D); + return aRetval; } } + } +#endif -} // end of namespace + // default: create VclPixelProcessor2D + // NOTE: Since this uses VCL OutputDevice in the VclPixelProcessor2D + // taking care of virtual devices is not needed, OutputDevice + // and VclPixelProcessor2D will traditionally take care of it + return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev); +} + +std::unique_ptr<BaseProcessor2D> createProcessor2DFromOutputDevice( + OutputDevice& rTargetOutDev, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) +{ + const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile(); + const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() + && !pMetaFile->IsPause()); + + if (bOutputToRecordingMetaFile) + { + // create MetaFile Vcl-Processor and process + return std::make_unique<VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev); + } + else + { + // create Pixel Vcl-Processor + return createPixelProcessor2DFromOutputDevice(rTargetOutDev, rViewInformation2D); + } +} +Bitmap extractBitmapFromBaseProcessor2D(const std::unique_ptr<BaseProcessor2D>& rProcessor) +{ + Bitmap aRetval; + +#if USE_HEADLESS_CODE + // currently only defined for cairo + CairoPixelProcessor2D* pSource(dynamic_cast<CairoPixelProcessor2D*>(rProcessor.get())); + + if (nullptr != pSource) + aRetval = pSource->extractBitmap(); +#endif + + // avoid unused parameter errors + (void)rProcessor; + + // default: return empty Bitmap + return aRetval; +} + +} // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/processorfromoutputdevice.cxx b/drawinglayer/source/processor2d/processorfromoutputdevice.cxx deleted file mode 100644 index c8433753aeff..000000000000 --- a/drawinglayer/source/processor2d/processorfromoutputdevice.cxx +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- 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 <vcl/outdev.hxx> -#include <vcl/gdimtf.hxx> -#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> -#include "vclmetafileprocessor2d.hxx" -#include "vclpixelprocessor2d.hxx" - -using namespace com::sun::star; - -namespace drawinglayer::processor2d -{ - std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> createBaseProcessor2DFromOutputDevice( - OutputDevice& rTargetOutDev, - const drawinglayer::geometry::ViewInformation2D& rViewInformation2D) - { - const GDIMetaFile* pMetaFile = rTargetOutDev.GetConnectMetaFile(); - const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); - - if(bOutputToRecordingMetaFile) - { - // create MetaFile Vcl-Processor and process - return std::make_unique<drawinglayer::processor2d::VclMetafileProcessor2D>(rViewInformation2D, rTargetOutDev); - } - else - { - // create Pixel Vcl-Processor - return std::make_unique<drawinglayer::processor2d::VclPixelProcessor2D>(rViewInformation2D, rTargetOutDev); - } - } -} // end of namespace - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx index e3a584f86172..4315f6696f75 100644 --- a/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx +++ b/drawinglayer/source/processor2d/textaspolygonextractor2d.cxx @@ -19,65 +19,56 @@ #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> namespace drawinglayer::processor2d { + void TextAsPolygonExtractor2D::processTextPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + { + // PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D + // TextDecoratedPortionPrimitive2D can produce the following primitives + // when being decomposed: + // + // - TextSimplePortionPrimitive2D + // - PolygonWavePrimitive2D + // - PolygonStrokePrimitive2D + // - PolygonStrokePrimitive2D + // - PolyPolygonColorPrimitive2D + // - PolyPolygonHairlinePrimitive2D + // - PolygonHairlinePrimitive2D + // - ShadowPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - TextEffectPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - GroupPrimitive2D + + // PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D + // TextSimplePortionPrimitive2D can produce the following primitives + // when being decomposed: + // + // - PolyPolygonColorPrimitive2D + // - TextEffectPrimitive2D + // - ModifiedColorPrimitive2D + // - TransformPrimitive2D + // - GroupPrimitive2D + + // encapsulate with flag and use decomposition + mnInText++; + process(rCandidate); + mnInText--; + } + void TextAsPolygonExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { - switch(rCandidate.getPrimitive2DID()) + switch (rCandidate.getPrimitive2DID()) { - case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : - { - // TextDecoratedPortionPrimitive2D can produce the following primitives - // when being decomposed: - // - // - TextSimplePortionPrimitive2D - // - PolygonWavePrimitive2D - // - PolygonStrokePrimitive2D - // - PolygonStrokePrimitive2D - // - PolyPolygonColorPrimitive2D - // - PolyPolygonHairlinePrimitive2D - // - PolygonHairlinePrimitive2D - // - ShadowPrimitive2D - // - ModifiedColorPrimitive2D - // - TransformPrimitive2D - // - TextEffectPrimitive2D - // - ModifiedColorPrimitive2D - // - TransformPrimitive2D - // - GroupPrimitive2D - - // encapsulate with flag and use decomposition - mnInText++; - process(rCandidate); - mnInText--; - - break; - } - case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : - { - // TextSimplePortionPrimitive2D can produce the following primitives - // when being decomposed: - // - // - PolyPolygonColorPrimitive2D - // - TextEffectPrimitive2D - // - ModifiedColorPrimitive2D - // - TransformPrimitive2D - // - GroupPrimitive2D - - // encapsulate with flag and use decomposition - mnInText++; - process(rCandidate); - mnInText--; - - break; - } - // as can be seen from the TextSimplePortionPrimitive2D and the // TextDecoratedPortionPrimitive2D, inside of the mnInText marks // the following primitives can occur containing geometry data @@ -177,48 +168,27 @@ namespace drawinglayer::processor2d const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // create new transformations for CurrentTransformation and for local ViewInformation2D - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), - getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), - getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process content process(rTransformCandidate.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); break; } - // ignorable primitives - case PRIMITIVE2D_ID_SCENEPRIMITIVE2D : - case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : - case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : - case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : - case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : - case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : - case PRIMITIVE2D_ID_MASKPRIMITIVE2D : - { + default: + TextExtractor2D::processBasePrimitive2D(rCandidate); break; - } - - default : - { - // process recursively - process(rCandidate); - break; - } } } TextAsPolygonExtractor2D::TextAsPolygonExtractor2D(const geometry::ViewInformation2D& rViewInformation) - : BaseProcessor2D(rViewInformation), - maTarget(), + : TextExtractor2D(rViewInformation), maBColorModifierStack(), mnInText(0) { diff --git a/drawinglayer/source/processor2d/textextractor2d.cxx b/drawinglayer/source/processor2d/textextractor2d.cxx new file mode 100644 index 000000000000..835cb0cc24bc --- /dev/null +++ b/drawinglayer/source/processor2d/textextractor2d.cxx @@ -0,0 +1,88 @@ +/* -*- 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 <drawinglayer/processor2d/textextractor2d.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> + +namespace drawinglayer::processor2d +{ +void TextExtractor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + switch (rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: + { + processTextPrimitive2D(rCandidate); + break; + } + + // usage of transformation stack is needed + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: + { + // remember current transformation and ViewInformation + const primitive2d::TransformPrimitive2D& rTransformCandidate( + static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); + const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); + + // create new transformations for CurrentTransformation and for local ViewInformation2D + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation( + getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); + + // process content + process(rTransformCandidate.getChildren()); + + // restore transformations + setViewInformation2D(aLastViewInformation2D); + + break; + } + + // ignorable primitives + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D: + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D: + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: + case PRIMITIVE2D_ID_MASKPRIMITIVE2D: + break; + + default: + { + // process recursively + process(rCandidate); + break; + } + } +} + +TextExtractor2D::TextExtractor2D(const geometry::ViewInformation2D& rViewInformation) + : BaseProcessor2D(rViewInformation) +{ +} + +TextExtractor2D::~TextExtractor2D() {} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx index 647825959108..d62606ac803b 100644 --- a/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx +++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.cxx @@ -19,7 +19,6 @@ #include <sal/config.h> #include <sal/log.hxx> -#include <osl/diagnose.h> #include <algorithm> #include <map> @@ -27,32 +26,43 @@ #include "vclhelperbufferdevice.hxx" #include <basegfx/range/b2drange.hxx> -#include <vcl/bitmapex.hxx> +#include <vcl/alpha.hxx> +#include <vcl/bitmap.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> -#include <tools/stream.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> #include <vcl/timer.hxx> -#include <cppuhelper/basemutex.hxx> -#include <vcl/lazydelete.hxx> +#include <tools/lazydelete.hxx> #include <vcl/dibtools.hxx> +#include <vcl/skia/SkiaHelper.hxx> +#include <mutex> -// buffered VDev usage +#ifdef DBG_UTIL +#include <o3tl/environment.hxx> +#include <tools/stream.hxx> +#endif +// #define SPEED_COMPARE +#ifdef SPEED_COMPARE +#include <tools/time.hxx> +#endif + +// buffered VDev usage namespace { -class VDevBuffer : public Timer, protected cppu::BaseMutex +class VDevBuffer : public Timer { private: struct Entry { VclPtr<VirtualDevice> buf; - bool isTransparent = false; - Entry(const VclPtr<VirtualDevice>& vdev, bool bTransparent) + Entry(const VclPtr<VirtualDevice>& vdev) : buf(vdev) - , isTransparent(bTransparent) { } }; + std::mutex m_aMutex; + // available buffers std::vector<Entry> maFreeBuffers; @@ -64,12 +74,13 @@ private: // virtualdevice because that isn't safe to do at least for Gtk2 std::map<VclPtr<VirtualDevice>, VclPtr<OutputDevice>> maDeviceTemplates; + static bool isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& size); + public: VDevBuffer(); virtual ~VDevBuffer() override; - VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, - bool bMonoChrome, bool bTransparent); + VclPtr<VirtualDevice> alloc(OutputDevice& rOutDev, const Size& rSizePixel); void free(VirtualDevice& rDevice); // Timer virtuals @@ -77,17 +88,14 @@ public: }; VDevBuffer::VDevBuffer() - : Timer("VDevBuffer timer") - , maFreeBuffers() - , maUsedBuffers() + : Timer("drawinglayer::VDevBuffer via Invoke()") { SetTimeout(10L * 1000L); // ten seconds - SetDebugName("drawinglayer::VDevBuffer via Invoke()"); } VDevBuffer::~VDevBuffer() { - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); Stop(); while (!maFreeBuffers.empty()) @@ -103,13 +111,40 @@ VDevBuffer::~VDevBuffer() } } -VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel, bool bClear, - bool bMonoChrome, bool bTransparent) +bool VDevBuffer::isSizeSuitable(const VclPtr<VirtualDevice>& device, const Size& rSizePixel) +{ + if (device->GetOutputWidthPixel() >= rSizePixel.getWidth() + && device->GetOutputHeightPixel() >= rSizePixel.getHeight()) + { + bool requireSmall = false; +#if defined(UNX) + // HACK: See the small size handling in SvpSalVirtualDevice::CreateSurface(). + // Make sure to not reuse a larger device when a small one should be preferred. + if (device->GetRenderBackendName() == "svp") + requireSmall = true; +#endif + // The same for Skia, see renderMethodToUseForSize(). + if (SkiaHelper::isVCLSkiaEnabled()) + requireSmall = true; + if (requireSmall) + { + if (rSizePixel.getWidth() <= 32 && rSizePixel.getHeight() <= 32 + && (device->GetOutputWidthPixel() > 32 || device->GetOutputHeightPixel() > 32)) + { + return false; + } + } + return true; + } + return false; +} + +VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSizePixel) { - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); VclPtr<VirtualDevice> pRetval; - sal_Int32 nBits = bMonoChrome ? 1 : rOutDev.GetBitCount(); + sal_Int32 nBits = rOutDev.GetBitCount(); bool bOkay(false); if (!maFreeBuffers.empty()) @@ -120,7 +155,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize { assert(a->buf && "Empty pointer in VDevBuffer (!)"); - if (nBits == a->buf->GetBitCount() && bTransparent == a->isTransparent) + if (nBits == a->buf->GetBitCount()) { // candidate is valid due to bit depth if (aFound != maFreeBuffers.end()) @@ -129,9 +164,7 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize if (bOkay) { // found is valid - const bool bCandidateOkay( - a->buf->GetOutputWidthPixel() >= rSizePixel.getWidth() - && a->buf->GetOutputHeightPixel() >= rSizePixel.getHeight()); + const bool bCandidateOkay = isSizeSuitable(a->buf, rSizePixel); if (bCandidateOkay) { @@ -156,16 +189,14 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize { // found is invalid, use candidate aFound = a; - bOkay = aFound->buf->GetOutputWidthPixel() >= rSizePixel.getWidth() - && aFound->buf->GetOutputHeightPixel() >= rSizePixel.getHeight(); + bOkay = isSizeSuitable(aFound->buf, rSizePixel); } } else { // none yet, use candidate aFound = a; - bOkay = aFound->buf->GetOutputWidthPixel() >= rSizePixel.getWidth() - && aFound->buf->GetOutputHeightPixel() >= rSizePixel.getHeight(); + bOkay = isSizeSuitable(aFound->buf, rSizePixel); } } } @@ -192,15 +223,12 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize { if (bOkay) { - if (bClear) - { - pRetval->Erase( - ::tools::Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight())); - } + pRetval->Erase(pRetval->PixelToLogic( + tools::Rectangle(0, 0, rSizePixel.getWidth(), rSizePixel.getHeight()))); } else { - pRetval->SetOutputSizePixel(rSizePixel, bClear); + pRetval->SetOutputSizePixel(rSizePixel, true); } } } @@ -208,11 +236,9 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize // no success yet, create new buffer if (!pRetval) { - pRetval = VclPtr<VirtualDevice>::Create( - rOutDev, bMonoChrome ? DeviceFormat::BITMASK : DeviceFormat::DEFAULT, - bTransparent ? DeviceFormat::DEFAULT : DeviceFormat::NONE); + pRetval = VclPtr<VirtualDevice>::Create(rOutDev, DeviceFormat::WITHOUT_ALPHA); maDeviceTemplates[pRetval] = &rOutDev; - pRetval->SetOutputSizePixel(rSizePixel, bClear); + pRetval->SetOutputSizePixel(rSizePixel, true); } else { @@ -222,14 +248,14 @@ VclPtr<VirtualDevice> VDevBuffer::alloc(OutputDevice& rOutDev, const Size& rSize } // remember allocated buffer - maUsedBuffers.emplace_back(pRetval, bTransparent); + maUsedBuffers.emplace_back(pRetval); return pRetval; } void VDevBuffer::free(VirtualDevice& rDevice) { - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); const auto aUsedFound = std::find_if(maUsedBuffers.begin(), maUsedBuffers.end(), [&rDevice](const Entry& el) { return el.buf == &rDevice; }); @@ -247,7 +273,7 @@ void VDevBuffer::free(VirtualDevice& rDevice) void VDevBuffer::Invoke() { - ::osl::MutexGuard aGuard(m_aMutex); + std::unique_lock aGuard(m_aMutex); while (!maFreeBuffers.empty()) { @@ -257,63 +283,138 @@ void VDevBuffer::Invoke() maFreeBuffers.pop_back(); } } -} -// support for rendering Bitmap and BitmapEx contents +#ifdef SPEED_COMPARE +void doSpeedCompare(double fTrans, const Bitmap& rContent, const tools::Rectangle& rDestPixel, + OutputDevice& rOutDev) +{ + const int nAvInd(500); + static double fFactors[nAvInd]; + static int nIndex(nAvInd + 1); + static int nRepeat(5); + static int nWorseTotal(0); + static int nBetterTotal(0); + int a(0); + + const Size aSizePixel(rDestPixel.GetSize()); + + // init statics + if (nIndex > nAvInd) + { + for (a = 0; a < nAvInd; a++) + fFactors[a] = 1.0; + nIndex = 0; + } + + // get start time + const sal_uInt64 nTimeA(tools::Time::GetSystemTicks()); + + // loop nRepeat times to get somewhat better timings, else + // numbers are pretty small + for (a = 0; a < nRepeat; a++) + { + // "Former" method using a temporary AlphaMask & DrawBitmapEx + sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0))); + const AlphaMask aAlphaMask(aSizePixel, &nMaskValue); + rOutDev.DrawBitmapEx(rDestPixel.TopLeft(), Bitmap(rContent, aAlphaMask)); + } + + // get intermediate time + const sal_uInt64 nTimeB(tools::Time::GetSystemTicks()); + + // loop nRepeat times + for (a = 0; a < nRepeat; a++) + { + // New method using DrawTransformedBitmapEx & fTrans directly + rOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix( + aSizePixel.Width(), aSizePixel.Height(), + rDestPixel.TopLeft().X(), rDestPixel.TopLeft().Y()), + rContent, 1 - fTrans); + } + + // get end time + const sal_uInt64 nTimeC(tools::Time::GetSystemTicks()); + // calculate deltas + const sal_uInt64 nTimeFormer(nTimeB - nTimeA); + const sal_uInt64 nTimeNew(nTimeC - nTimeB); + + // compare & note down + if (nTimeFormer != nTimeNew && 0 != nTimeFormer && 0 != nTimeNew) + { + if ((nTimeFormer < 10 || nTimeNew < 10) && nRepeat < 500) + { + nRepeat += 1; + SAL_INFO("drawinglayer.processor2d", "Increment nRepeat to " << nRepeat); + return; + } + + const double fNewFactor((double)nTimeFormer / nTimeNew); + fFactors[nIndex % nAvInd] = fNewFactor; + nIndex++; + double fAverage(0.0); + { + for (a = 0; a < nAvInd; a++) + fAverage += fFactors[a]; + fAverage /= nAvInd; + } + if (fNewFactor < 1.0) + nWorseTotal++; + else + nBetterTotal++; + + char buf[300]; + sprintf(buf, + "Former: %ld New: %ld It got %s (factor %f) (av. last %d Former/New is %f, " + "WorseTotal: %d, BetterTotal: %d)", + nTimeFormer, nTimeNew, fNewFactor < 1.0 ? "WORSE" : "BETTER", + fNewFactor < 1.0 ? 1.0 / fNewFactor : fNewFactor, nAvInd, fAverage, nWorseTotal, + nBetterTotal); + SAL_INFO("drawinglayer.processor2d", buf); + } +} +#endif +} + +// support for rendering Bitmap contents namespace drawinglayer { -// static global VDev buffer for the VclProcessor2D's (VclMetafileProcessor2D and VclPixelProcessor2D) +// static global VDev buffer for VclProcessor2D/VclPixelProcessor2D VDevBuffer& getVDevBuffer() { // secure global instance with Vcl's safe destroyer of external (seen by // library base) stuff, the remembered VDevs need to be deleted before // Vcl's deinit - static vcl::DeleteOnDeinit<VDevBuffer> aVDevBuffer(new VDevBuffer()); + static tools::DeleteOnDeinit<VDevBuffer> aVDevBuffer{}; return *aVDevBuffer.get(); } -impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, - bool bContentTransparent) +impBufferDevice::impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange) : mrOutDev(rOutDev) , mpContent(nullptr) - , mpMask(nullptr) , mpAlpha(nullptr) - , mbContentTransparent(bContentTransparent) { basegfx::B2DRange aRangePixel(rRange); aRangePixel.transform(mrOutDev.GetViewTransformation()); - const ::tools::Rectangle aRectPixel(static_cast<sal_Int32>(floor(aRangePixel.getMinX())), - static_cast<sal_Int32>(floor(aRangePixel.getMinY())), - static_cast<sal_Int32>(ceil(aRangePixel.getMaxX())), - static_cast<sal_Int32>(ceil(aRangePixel.getMaxY()))); - const Point aEmptyPoint; - maDestPixel = ::tools::Rectangle(aEmptyPoint, mrOutDev.GetOutputSizePixel()); - maDestPixel.Intersection(aRectPixel); + maDestPixel = tools::Rectangle(floor(aRangePixel.getMinX()), floor(aRangePixel.getMinY()), + ceil(aRangePixel.getMaxX()), ceil(aRangePixel.getMaxY())); + maDestPixel.Intersection(tools::Rectangle{ Point{}, mrOutDev.GetOutputSizePixel() }); if (!isVisible()) return; -#ifdef IOS - // Exact mechanism unknown, but for some reason SmartArt - // rendering, especially shadows, is broken on iOS unless - // we pass 'true' here. Are virtual devices always de - // facto cleared when created on other platforms? - mpContent - = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false, bContentTransparent); -#else - mpContent - = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), false, false, bContentTransparent); -#endif + mpContent = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize()); // #i93485# assert when copying from window to VDev is used SAL_WARN_IF( mrOutDev.GetOutDevType() == OUTDEV_WINDOW, "drawinglayer", "impBufferDevice render helper: Copying from Window to VDev, this should be avoided (!)"); + // initialize buffer by blitting content of source to prepare for + // transparence/ copying back const bool bWasEnabledSrc(mrOutDev.IsMapModeEnabled()); mrOutDev.EnableMapMode(false); - mpContent->DrawOutDev(aEmptyPoint, maDestPixel.GetSize(), maDestPixel.TopLeft(), + mpContent->DrawOutDev(Point(), maDestPixel.GetSize(), maDestPixel.TopLeft(), maDestPixel.GetSize(), mrOutDev); mrOutDev.EnableMapMode(bWasEnabledSrc); @@ -338,11 +439,6 @@ impBufferDevice::~impBufferDevice() getVDevBuffer().free(*mpContent); } - if (mpMask) - { - getVDevBuffer().free(*mpMask); - } - if (mpAlpha) { getVDevBuffer().free(*mpAlpha); @@ -357,23 +453,18 @@ void impBufferDevice::paint(double fTrans) const Point aEmptyPoint; const Size aSizePixel(maDestPixel.GetSize()); const bool bWasEnabledDst(mrOutDev.IsMapModeEnabled()); -#ifdef DBG_UTIL - static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore -#endif mrOutDev.EnableMapMode(false); mpContent->EnableMapMode(false); #ifdef DBG_UTIL - if (bDoSaveForVisualControl) + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + + if (!sDumpPath.isEmpty() && bDoSaveForVisualControl) { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\content.bmp", -#else - "~/content.bmp", -#endif - StreamMode::WRITE | StreamMode::TRUNC); + SvFileStream aNew(sDumpPath + "content.bmp", StreamMode::WRITE | StreamMode::TRUNC); Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel)); WriteDIB(aContent, aNew, false, true); } @@ -387,67 +478,87 @@ void impBufferDevice::paint(double fTrans) { mpAlpha->EnableMapMode(false); AlphaMask aAlphaMask(mpAlpha->GetBitmap(aEmptyPoint, aSizePixel)); + aAlphaMask.Invert(); // convert transparency to alpha #ifdef DBG_UTIL - if (bDoSaveForVisualControl) + if (!sDumpPath.isEmpty() && bDoSaveForVisualControl) { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\transparence.bmp", -#else - "~/transparence.bmp", -#endif - StreamMode::WRITE | StreamMode::TRUNC); + SvFileStream aNew(sDumpPath + "transparence.bmp", + StreamMode::WRITE | StreamMode::TRUNC); WriteDIB(aAlphaMask.GetBitmap(), aNew, false, true); } #endif - BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel)); - if (mbContentTransparent) - aAlphaMask.BlendWith(aContent.GetAlpha()); - mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlphaMask)); + Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel)); + mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), Bitmap(aContent, aAlphaMask)); } - else if (mpMask) + else if (0.0 != fTrans) { - mpMask->EnableMapMode(false); - const Bitmap aMask(mpMask->GetBitmap(aEmptyPoint, aSizePixel)); + const Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel)); -#ifdef DBG_UTIL - if (bDoSaveForVisualControl) - { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\mask.bmp", -#else - "~/mask.bmp", -#endif - StreamMode::WRITE | StreamMode::TRUNC); - WriteDIB(aMask, aNew, false, true); - } -#endif +#ifdef SPEED_COMPARE + static bool bCompareFormerAndNewTimings(true); - if (mbContentTransparent) + if (bCompareFormerAndNewTimings) { - BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel)); - AlphaMask aAlpha(aContent.GetAlpha()); - aAlpha.BlendWith(aMask); - mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlpha)); + doSpeedCompare(fTrans, aContent, maDestPixel, mrOutDev); } else +#endif + // Note: this extra scope is needed due to 'clang plugin indentation'. It complains + // that lines 494 and (now) 539 are 'statement mis-aligned compared to neighbours'. + // That is true if SPEED_COMPARE is not defined. Not nice, but have to fix this. { - Bitmap aContent(mpContent->GetBitmap(aEmptyPoint, aSizePixel)); - mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent, aMask)); + // For the case we have a unified transparency value there is a former + // and new method to paint that which can be used. To decide on measurements, + // I added 'doSpeedCompare' above which can be activated by defining + // SPEED_COMPARE at the top of this file. + // I added the used Testdoc: blurplay3.odg as + // https://bugs.documentfoundation.org/attachment.cgi?id=182463 + // I did measure on + // + // Linux Dbg: + // Former: 21 New: 32 It got WORSE (factor 1.523810) (av. last 500 Former/New is 0.968533, WorseTotal: 515, BetterTotal: 934) + // + // Linux Pro: + // Former: 27 New: 44 It got WORSE (factor 1.629630) (av. last 500 Former/New is 0.923256, WorseTotal: 433, BetterTotal: 337) + // + // Win Dbg: + // Former: 21 New: 78 It got WORSE (factor 3.714286) (av. last 500 Former/New is 1.007176, WorseTotal: 85, BetterTotal: 1428) + // + // Win Pro: + // Former: 3 New: 4 It got WORSE (factor 1.333333) (av. last 500 Former/New is 1.054167, WorseTotal: 143, BetterTotal: 3909) + // + // Note: I am aware that the Dbg are of limited usefulness, but include them here + // for reference. + // + // The important part is "av. last 500 Former/New is %ld" which describes the averaged factor from Former/New + // over the last 500 measurements. When < 1.0 Former is better (Linux), > 1.0 (Win) New is better. Since the + // factor on Win is still close to 1.0 what means we lose nearly nothing and Linux Former is better, I will + // use Former for now. + // + // To easily allow to change this (maybe system-dependent) I add a static switch here, + // also for eventually experimenting (hint: can be changed in the debugger). + static bool bUseNew(false); + + if (bUseNew) + { + // New method using DrawTransformedBitmapEx & fTrans directly + mrOutDev.DrawTransformedBitmapEx(basegfx::utils::createScaleTranslateB2DHomMatrix( + aSizePixel.Width(), aSizePixel.Height(), + maDestPixel.TopLeft().X(), + maDestPixel.TopLeft().Y()), + aContent, 1 - fTrans); + } + else + { + // "Former" method using a temporary AlphaMask & DrawBitmapEx + sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0))); + const AlphaMask aAlphaMask(aSizePixel, &nMaskValue); + mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), Bitmap(aContent, aAlphaMask)); + } } } - else if (0.0 != fTrans) - { - sal_uInt8 nMaskValue(static_cast<sal_uInt8>(basegfx::fround(fTrans * 255.0))); - AlphaMask aAlphaMask(aSizePixel, &nMaskValue); - BitmapEx aContent(mpContent->GetBitmapEx(aEmptyPoint, aSizePixel)); - if (mbContentTransparent) - aAlphaMask.BlendWith(aContent.GetAlpha()); - mrOutDev.DrawBitmapEx(maDestPixel.TopLeft(), BitmapEx(aContent.GetBitmap(), aAlphaMask)); - } else { mrOutDev.DrawOutDev(maDestPixel.TopLeft(), aSizePixel, aEmptyPoint, aSizePixel, *mpContent); @@ -464,28 +575,13 @@ VirtualDevice& impBufferDevice::getContent() return *mpContent; } -VirtualDevice& impBufferDevice::getMask() -{ - SAL_WARN_IF(!mpContent, "drawinglayer", - "impBufferDevice: No content, check isVisible() before accessing (!)"); - if (!mpMask) - { - mpMask = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, true, false); - mpMask->SetMapMode(mpContent->GetMapMode()); - - // do NOT copy AA flag for mask! - } - - return *mpMask; -} - VirtualDevice& impBufferDevice::getTransparence() { SAL_WARN_IF(!mpContent, "drawinglayer", "impBufferDevice: No content, check isVisible() before accessing (!)"); if (!mpAlpha) { - mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize(), true, false, false); + mpAlpha = getVDevBuffer().alloc(mrOutDev, maDestPixel.GetSize()); mpAlpha->SetMapMode(mpContent->GetMapMode()); // copy AA flag for new target; masking needs to be smooth diff --git a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx index c85e3c4a6048..618fb38209a5 100644 --- a/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx +++ b/drawinglayer/source/processor2d/vclhelperbufferdevice.hxx @@ -21,12 +21,73 @@ #include <vcl/virdev.hxx> -namespace basegfx -{ -class B2DRange; -} - -// support methods for vcl direct gradient rendering +// Helper class *exclusively* for VclProcessor2D. It should only +// be used internally, see current four usages. It is used to +// render something with mask or transparence (see MaskPrimitive2D, +// UnifiedTransparencePrimitive2D and TransparencePrimitive2D) or +// as tooling for preparing pixelized output in the renderer +// (see PatternFillPrimitive2D) if that is faster. +// +// To do so, initializing this instance takes over a lot of work +// from you: +// - It initializes a 'Content' VDev which is buffered (since it had +// shown that re-allocating all the time is slower). It checks +// visibility and all usages initializing this should check for +// isVisible() after construction. +// - It pre-initializes the 'Content' VDev with blitting the content +// of the target VDev. +// - It offers to get a 'Transparence' VDev (also from the buffer) if +// needed. +// +// If 'Transparence' is/was used, it combines as needed to paint +// all buffered stuff to target VDev when calling paint(). +// Caution: It is designed to use *either* a fixed transparence in +// the paint()-call *or* a fill TransparenceChannel using a +// 'Transparence' VDev. It is *not* designed to use/combine +// both - it's simply not needed for it's intended purpose/usage. +// +// Painting transparent works based on a simple principle: It first +// blits the original content of the target VDev. Then the content +// is painted on top of that, plus a Transparence/Mask prepared. +// The combination always works since unchanged pixels will not +// change, independent of the transparence value [0..255] it gets +// mixed with. Or the other way around: Only pixels changed on the +// Content *can* be changed by transparence values. +// +// This is 2.5 times faster than first painting to a +// 'combined' VDev that supports transparency, as is used by the +// presentation engine. +// For the mentioned factor refer to: +// Patch to demonstrate former and now repaint differences +// https://gerrit.libreoffice.org/c/core/+/129301 +// git fetch https://git.libreoffice.org/core refs/changes/01/129301/3 && git cherry-pick FETCH_HEAD +// +// Note: This principle only works when the target is RGB, so +// useful for EditViews like for PrimitiveRenderers where this is +// the case. For usage with GBA targets the starting conditions +// would have to be modified to not blend against the initial color +// of 'Content' (usually COL_WHITE). +// +// After having finished the rework of ShadowPrimitive2D, +// SoftEdgePrimitive2D and GlowPrimitive2D (see commits:) +// e735ad1c57cddaf17d6ffc0cf15b5e14fa63c4ad +// 707b0c328a282d993fa33b618083d20b6c521de6 +// c2d1458723c66c2fd717a112f89f773226adc841 +// which used the impBufferDevice in such a mode combined with +// mentioned 'combined' transparence VDev it is now possible +// to return to this former, much faster method. +// +// Please do *not* hack/use this helper class, better create +// a new one fitting your/the intended purpose. I do not want +// to see losing performance by this getting modified again. +// +// Note: Using that 'combined' transparence VDev is not really +// recommended, it may vanish anytime. That it works with +// PrimitiveRenderers *at all* is neither designed nor tested +// or recommended - it's pure coincidence. +// +// I hope that for the future all this will vanish by getting to +// fully RGBA-capable devices - what is planned and makes sense. namespace drawinglayer { @@ -34,20 +95,16 @@ class impBufferDevice { OutputDevice& mrOutDev; VclPtr<VirtualDevice> mpContent; - VclPtr<VirtualDevice> mpMask; VclPtr<VirtualDevice> mpAlpha; tools::Rectangle maDestPixel; - bool mbContentTransparent; public: - impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange, - bool bContentTransparent = false); + impBufferDevice(OutputDevice& rOutDev, const basegfx::B2DRange& rRange); ~impBufferDevice(); void paint(double fTrans = 0.0); bool isVisible() const { return !maDestPixel.IsEmpty(); } VirtualDevice& getContent(); - VirtualDevice& getMask(); VirtualDevice& getTransparence(); }; } // end of namespace drawinglayer diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index 4c4a9fe3a329..6c8160f09a70 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -17,38 +17,37 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include <cmath> #include <memory> #include "vclmetafileprocessor2d.hxx" #include "vclpixelprocessor2d.hxx" #include <rtl/ustring.hxx> -#include <rtl/math.hxx> #include <tools/gen.hxx> #include <tools/stream.hxx> -#include <tools/diagnose_ex.h> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/flagguard.hxx> #include <comphelper/processfactory.hxx> +#include <config_global.h> #include <basegfx/polygon/b2dpolygonclipper.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dlinegeometry.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <vcl/virdev.hxx> #include <vcl/gdimtf.hxx> #include <vcl/gradient.hxx> #include <vcl/graphictools.hxx> #include <vcl/metaact.hxx> #include <vcl/graph.hxx> // for PDFExtOutDevData Graphic support -#include <toolkit/helper/formpdfexport.hxx> // for PDFExtOutDevData Graphic support +#include <vcl/formpdfexport.hxx> // for PDFExtOutDevData Graphic support #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/glowprimitive2d.hxx> -#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> @@ -65,6 +64,10 @@ #include <drawinglayer/primitive2d/epsprimitive2d.hxx> #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for Title/Description metadata +#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> +#include <drawinglayer/converters.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <tools/vcompat.hxx> #include <com/sun/star/awt/XControl.hpp> #include <com/sun/star/i18n/BreakIterator.hpp> @@ -180,7 +183,7 @@ void fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon) if (aSplitted.count() != nPolyCount) { - rPolyPolygon = aSplitted; + rPolyPolygon = std::move(aSplitted); } } @@ -224,8 +227,10 @@ VclMetafileProcessor2D::impDumpToMetaFile(const primitive2d::Primitive2DContaine aPrimitiveRange.transform(maCurrentTransformation); const tools::Rectangle aPrimitiveRectangle( - basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()), - basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY())); + basegfx::fround<tools::Long>(aPrimitiveRange.getMinX()), + basegfx::fround<tools::Long>(aPrimitiveRange.getMinY()), + basegfx::fround<tools::Long>(aPrimitiveRange.getMaxX()), + basegfx::fround<tools::Long>(aPrimitiveRange.getMaxY())); ScopedVclPtrInstance<VirtualDevice> aContentVDev; MapMode aNewMapMode(pLastOutputDevice->GetMapMode()); @@ -260,22 +265,24 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( Gradient& o_rVCLGradient, const attribute::FillGradientAttribute& rFiGrAtt, bool bIsTransparenceGradient) const { + const basegfx::BColor aStartColor(rFiGrAtt.getColorStops().front().getStopColor()); + const basegfx::BColor aEndColor(rFiGrAtt.getColorStops().back().getStopColor()); + if (bIsTransparenceGradient) { // it's about transparence channel intensities (black/white), do not use color modifier - o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor())); - o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor())); + o_rVCLGradient.SetStartColor(Color(aStartColor)); + o_rVCLGradient.SetEndColor(Color(aEndColor)); } else { // use color modifier to influence start/end color of gradient - o_rVCLGradient.SetStartColor( - Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor()))); - o_rVCLGradient.SetEndColor( - Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor()))); + o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(aStartColor))); + o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(aEndColor))); } - o_rVCLGradient.SetAngle(static_cast<sal_uInt16>(rFiGrAtt.getAngle() * (1.0 / F_PI1800))); + o_rVCLGradient.SetAngle( + Degree10(static_cast<sal_uInt32>(basegfx::rad2deg<10>(rFiGrAtt.getAngle())))); o_rVCLGradient.SetBorder(static_cast<sal_uInt16>(rFiGrAtt.getBorder() * 100.0)); o_rVCLGradient.SetOfsX(static_cast<sal_uInt16>(rFiGrAtt.getOffsetX() * 100.0)); o_rVCLGradient.SetOfsY(static_cast<sal_uInt16>(rFiGrAtt.getOffsetY() * 100.0)); @@ -284,40 +291,7 @@ void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient( // defaults for intensity; those were computed into the start/end colors already o_rVCLGradient.SetStartIntensity(100); o_rVCLGradient.SetEndIntensity(100); - - switch (rFiGrAtt.getStyle()) - { - default: // attribute::GradientStyle::Linear : - { - o_rVCLGradient.SetStyle(GradientStyle::Linear); - break; - } - case attribute::GradientStyle::Axial: - { - o_rVCLGradient.SetStyle(GradientStyle::Axial); - break; - } - case attribute::GradientStyle::Radial: - { - o_rVCLGradient.SetStyle(GradientStyle::Radial); - break; - } - case attribute::GradientStyle::Elliptical: - { - o_rVCLGradient.SetStyle(GradientStyle::Elliptical); - break; - } - case attribute::GradientStyle::Square: - { - o_rVCLGradient.SetStyle(GradientStyle::Square); - break; - } - case attribute::GradientStyle::Rect: - { - o_rVCLGradient.SetStyle(GradientStyle::Rect); - break; - } - } + o_rVCLGradient.SetStyle(rFiGrAtt.getStyle()); } void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGraphicFill) @@ -328,7 +302,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill const* pSvtGr WriteSvtGraphicFill(aMemStm, *pSvtGraphicFill); mpMetaFile->AddAction(new MetaCommentAction( - "XPATHFILL_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + "XPATHFILL_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), aMemStm.TellEnd())); mnSvtGraphicFillCount++; } @@ -339,7 +313,7 @@ void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill const* pSvtGrap if (pSvtGraphicFill && mnSvtGraphicFillCount) { mnSvtGraphicFillCount--; - mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END")); + mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"_ostr)); } } @@ -386,36 +360,34 @@ std::unique_ptr<SvtGraphicStroke> VclMetafileProcessor2D::impTryToCreateSvtGraph if (!aLocalPolygon.isClosed()) { - double fPolyLength(0.0); - double fStart(0.0); - double fEnd(0.0); - - if (pStart && pStart->isActive()) + const bool bCalcStart = pStart && pStart->isActive(); + const bool bCalcEnd = pEnd && pEnd->isActive(); + if (bCalcStart || bCalcEnd) { - fPolyLength = basegfx::utils::getLength(aLocalPolygon); + double fPolyLength = basegfx::utils::getLength(aLocalPolygon); + double fStart(0.0); + double fEnd(0.0); - aStartArrow = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), - fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart); - } - - if (pEnd && pEnd->isActive()) - { - if (basegfx::fTools::equalZero(fPolyLength)) + if (bCalcStart) { - fPolyLength = basegfx::utils::getLength(aLocalPolygon); + aStartArrow = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(), + fPolyLength, pStart->isCentered() ? 0.5 : 0.0, &fStart); } - aEndArrow = basegfx::utils::createAreaGeometryForLineStartEnd( - aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), fPolyLength, - pEnd->isCentered() ? 0.5 : 0.0, &fEnd); - } + if (bCalcEnd) + { + aEndArrow = basegfx::utils::createAreaGeometryForLineStartEnd( + aLocalPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(), + fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, &fEnd); + } - if (0.0 != fStart || 0.0 != fEnd) - { - // build new poly, consume something from old poly - aLocalPolygon = basegfx::utils::getSnippetAbsolute(aLocalPolygon, fStart, - fPolyLength - fEnd, fPolyLength); + if (0.0 != fStart || 0.0 != fEnd) + { + // build new poly, consume something from old poly + aLocalPolygon = basegfx::utils::getSnippetAbsolute( + aLocalPolygon, fStart, fPolyLength - fEnd, fPolyLength); + } } } @@ -446,7 +418,8 @@ std::unique_ptr<SvtGraphicStroke> VclMetafileProcessor2D::impTryToCreateSvtGraph { eJoin = SvtGraphicStroke::joinMiter; // ATM 15 degrees is assumed - fMiterLength /= rtl::math::sin(basegfx::deg2rad(15.0)); + // TODO wait for P1383R0 and C++20's std::numbers::pi + fMiterLength /= std::sin(M_PI / 12); break; } case basegfx::B2DLineJoin::Round: @@ -497,7 +470,7 @@ std::unique_ptr<SvtGraphicStroke> VclMetafileProcessor2D::impTryToCreateSvtGraph pRetval.reset( new SvtGraphicStroke(tools::Polygon(aLocalPolygon), tools::PolyPolygon(aStartArrow), tools::PolyPolygon(aEndArrow), mfCurrentUnifiedTransparence, - fLineWidth, eCap, eJoin, fMiterLength, aDashArray)); + fLineWidth, eCap, eJoin, fMiterLength, std::move(aDashArray))); } return pRetval; @@ -511,7 +484,7 @@ void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke const* pS WriteSvtGraphicStroke(aMemStm, *pSvtGraphicStroke); mpMetaFile->AddAction(new MetaCommentAction( - "XPATHSTROKE_SEQ_BEGIN", 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + "XPATHSTROKE_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), aMemStm.TellEnd())); mnSvtGraphicStrokeCount++; } @@ -522,11 +495,11 @@ void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke const* pSvt if (pSvtGraphicStroke && mnSvtGraphicStrokeCount) { mnSvtGraphicStrokeCount--; - mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END")); + mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"_ostr)); } } -void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement eElem) +void VclMetafileProcessor2D::popStructureElement(vcl::pdf::StructElement eElem) { if (!maListElements.empty() && maListElements.top() == eElem) { @@ -537,19 +510,16 @@ void VclMetafileProcessor2D::popStructureElement(vcl::PDFWriter::StructElement e void VclMetafileProcessor2D::popListItem() { - popStructureElement(vcl::PDFWriter::LIBody); - popStructureElement(vcl::PDFWriter::ListItem); + popStructureElement(vcl::pdf::StructElement::LIBody); + popStructureElement(vcl::pdf::StructElement::ListItem); } void VclMetafileProcessor2D::popList() { popListItem(); - popStructureElement(vcl::PDFWriter::List); + popStructureElement(vcl::pdf::StructElement::List); } -// init static break iterator -uno::Reference<css::i18n::XBreakIterator> VclMetafileProcessor2D::mxBreakIterator; - VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev) : VclProcessor2D(rViewInformation, rOutDev) @@ -680,7 +650,7 @@ VclMetafileProcessor2D::~VclMetafileProcessor2D() Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to hold the original EPS which was imported in the same MetaFile as first 2 entries. Only used to export the original again (if exists). - Not necessary to support with MetaFuleRenderer. + Not necessary to support with MetaFileRenderer. XTEXT_SCROLLRECT, XTEXT_PAINTRECT Currently used to get extra MetaFile infos using GraphicExporter which again uses @@ -720,17 +690,7 @@ VclMetafileProcessor2D::~VclMetafileProcessor2D() - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the target and uno control data is created in UnoControlPDFExportContact::do_PaintObject. - This may be added in primitive MetaFile renderer. - Adding support... - OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace - svxform. Have to talk to FS if this has to be like that. Especially since - vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl. - Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move - that stuff to somewhere else, maybe tools or svtools ?!? We will see... - Moved to toolkit, so I have to link against it. I tried VCL first, but it did - not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other than the name - may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself, - the lowest movement plane is toolkit. + This was added in primitive MetaFile renderer. Checked form control export, it works well. Done. - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are @@ -829,7 +789,7 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: { - // direct draw of transformed BitmapEx primitive; use default processing, but without + // direct draw of transformed Bitmap primitive; use default processing, but without // former testing if graphic content is inside discrete local viewport; this is not // setup for metafile targets (metafile renderer tries to render in logic coordinates, // the mapping is kept to the OutputDevice for better Metafile recording) @@ -838,8 +798,17 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: { - processPolyPolygonGraphicPrimitive2D( - static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); + if (maBColorModifierStack.count()) + { + // tdf#151104 unfortunately processPolyPolygonGraphicPrimitive2D below + // does not support an active BColorModifierStack, so use the default + process(rCandidate); + } + else + { + processPolyPolygonGraphicPrimitive2D( + static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); + } break; } case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D: @@ -867,7 +836,7 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: { - // modified color group. Force output to unified color. Use default pocessing. + // modified color group. Force output to unified color. Use default processing. RenderModifiedColorPrimitive2D( static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); break; @@ -886,7 +855,7 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: { - // use default transform group pocessing + // use default transform group processing RenderTransformPrimitive2D( static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); break; @@ -900,14 +869,14 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: { - // use default marker array pocessing + // use default marker array processing RenderMarkerArrayPrimitive2D( static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); break; } case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: { - // use default point array pocessing + // use default point array processing RenderPointArrayPrimitive2D( static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); break; @@ -925,14 +894,21 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D: { - RenderObjectInfoPrimitive2D( + processObjectInfoPrimitive2D( static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate)); break; } - case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: - case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D: + case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: { - processPrimitive2DOnPixelProcessor(rCandidate); + processFillGraphicPrimitive2D( + static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D: + { + // EmphasisMarks are traditionally not added to Metafiles, see + // OutputDevice::ImplDrawEmphasisMarks which resets GDIMetaFile* + // while painting these, so just ignore these break; } default: @@ -944,6 +920,165 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi } } +void VclMetafileProcessor2D::processObjectInfoPrimitive2D( + primitive2d::ObjectInfoPrimitive2D const& rObjectInfoPrimitive2D) +{ + // tdf#154982 process content first, so this object overrides any nested one + process(rObjectInfoPrimitive2D.getChildren()); + + // currently StructureTagPrimitive2D is only used for SdrObjects - have to + // avoid adding Alt text if the SdrObject is not actually tagged, as it + // would then end up on an unrelated structure element. + if (mpCurrentStructureTag && mpCurrentStructureTag->isTaggedSdrObject()) + { + // Create image alternative description from ObjectInfoPrimitive2D info + // for PDF export, for the currently active SdrObject's structure element + if (mpPDFExtOutDevData->GetIsExportTaggedPDF()) + { + OUString aAlternateDescription; + + if (!rObjectInfoPrimitive2D.getTitle().isEmpty()) + { + aAlternateDescription += rObjectInfoPrimitive2D.getTitle(); + } + + if (!rObjectInfoPrimitive2D.getDesc().isEmpty()) + { + if (!aAlternateDescription.isEmpty()) + { + aAlternateDescription += " - "; + } + + aAlternateDescription += rObjectInfoPrimitive2D.getDesc(); + } + + // Use SetAlternateText to set it. This will work as long as some + // structure is used (see PDFWriterImpl::setAlternateText and + // m_nCurrentStructElement - tagged PDF export works with this in + // Draw/Impress/Writer, but not in Calc due to too less structure...) + //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..? + if (!aAlternateDescription.isEmpty()) + { + mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); + } + } + } +} + +void VclMetafileProcessor2D::processFillGraphicPrimitive2D( + primitive2d::FillGraphicPrimitive2D const& rFillGraphicPrimitive2D) +{ + // tdf#166709 check if we have to make an exception handling this + // FillGraphicPrimitive2D. If it + // - has transparency + // - is tiled + // - is a Bitmap + // - is not animated + // - is no embedded SVG + // we have to, see below + if (!basegfx::fTools::equalZero(rFillGraphicPrimitive2D.getTransparency(), 0.0)) + { + // we have transparency + const attribute::FillGraphicAttribute& rAttribute(rFillGraphicPrimitive2D.getFillGraphic()); + + if (rAttribute.getTiling()) + { + // we have tiling + const Graphic& rGraphic(rAttribute.getGraphic()); + + if (GraphicType::Bitmap == rGraphic.GetType() && !rGraphic.IsAnimated() + && !rGraphic.getVectorGraphicData()) + { + // tdf#166709 it is a Bitmap, not animated & not + // embedded SVG. + + // conditions are met. Unfortunately for metafile + // and for PDF export we *need* all tiles which are + // potentially created by the decomposition of the + // FillGraphicPrimitive2D to be embedded to a single + // UnifiedTransparencePrimitive2D that holds that + // transparency - as it was before adding more + // possibilities for direct unified transparency. + + // Despite the decomposition being correct and creating + // now BitmapAlphaPrimitive2D with every one holding the + // correct alpha, the 'old' way with encapsulating to + // a UnifiedTransparencePrimitive2D is needed here + // to create a single bitmap representation that then + // gets used. When not doing this a potentially high + // number of BitmapAlphaPrimitive2D will be exported, + // which is not an error but needs too much resources, + // prevents loading of the created PDF for some viewers + // and bloats the PDF file. + + // NOTE: I thought if only doing this for the PDF export + // case would make sense, but all exports still based + // on metafile potentially have this problem, so better + // do it in general at metafile creation already. + + // NOTE: This shows how urgent it would be to create a + // PDF export using a PrimitiveRenderer instead of + // Metafile - that could do the right thing and use + // a representation in the PDF that is capable of + // tiling. No chance to do that with the existing + // metafile stuff we have. + + // NOTE: The creation of the possible MetafileAction + // for this is done here locally in method + // processUnifiedTransparencePrimitive2D. Use that + // directly if necessary. + + // So: create a FillGraphicPrimitive2D without transparency + // embedded to a UnifiedTransparencePrimitive2D representing + // the transparency and process it directly + rtl::Reference<primitive2d::BasePrimitive2D> aPrimitive( + new primitive2d::UnifiedTransparencePrimitive2D( + primitive2d::Primitive2DContainer{ + rtl::Reference<primitive2d::FillGraphicPrimitive2D>( + new primitive2d::FillGraphicPrimitive2D( + rFillGraphicPrimitive2D.getTransformation(), + attribute::FillGraphicAttribute( + rGraphic, rAttribute.getGraphicRange(), + rAttribute.getTiling(), rAttribute.getOffsetX(), + rAttribute.getOffsetY()))) }, + rFillGraphicPrimitive2D.getTransparency())); + + // tdf#166709 see comments 21-23, we have two possibilities here: + // (a) use UnifiedTransparencePrimitive2D: test PDF is 47.5kb + // (b) use TransparencePrimitive2D: test PDF is 30.8 kb + // differences are described in the task. Due to (b) being smaller + // and is better re-loadable I opt for that. To be able to change + // this easily I let both versions stand here + static bool bTransparencePrimitive2DUse(true); + + if (bTransparencePrimitive2DUse) + { + // process recursively. Since this first gets the decomposition + // (else a primitive processor would loop recursively) this will + // use TransparencePrimitive2D, created by + // UnifiedTransparencePrimitive2D::get2DDecomposition. This could + // also be done here, but that decompose already has needed stuff + // and we keep it in one place + process(*aPrimitive); + } + else + { + // process UnifiedTransparencePrimitive2D primitive directly + processUnifiedTransparencePrimitive2D( + static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>( + *aPrimitive)); + } + + // we are done, return + return; + } + } + } + + // all other cases: process recursively with original primitive + process(rFillGraphicPrimitive2D); +} + void VclMetafileProcessor2D::processGraphicPrimitive2D( const primitive2d::GraphicPrimitive2D& rGraphicPrimitive) { @@ -1035,43 +1170,11 @@ void VclMetafileProcessor2D::processGraphicPrimitive2D( sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY()))); } - // Create image alternative description from ObjectInfoPrimitive2D info - // for PDF export - if (mpPDFExtOutDevData->GetIsExportTaggedPDF() && nullptr != getObjectInfoPrimitive2D()) - { - OUString aAlternateDescription; - - if (!getObjectInfoPrimitive2D()->getTitle().isEmpty()) - { - aAlternateDescription += getObjectInfoPrimitive2D()->getTitle(); - } - - if (!getObjectInfoPrimitive2D()->getDesc().isEmpty()) - { - if (!aAlternateDescription.isEmpty()) - { - aAlternateDescription += " - "; - } - - aAlternateDescription += getObjectInfoPrimitive2D()->getDesc(); - } - - // Use SetAlternateText to set it. This will work as long as some - // structure is used (see PDFWriterImpl::setAlternateText and - // m_nCurrentStructElement - tagged PDF export works with this in - // Draw/Impress/Writer, but not in Calc due to too less structure...) - //Z maybe add structure to Calc PDF export, may need some BeginGroup/EndGroup stuff ..? - if (!aAlternateDescription.isEmpty()) - { - mpPDFExtOutDevData->SetAlternateText(aAlternateDescription); - } - } - // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded, // uncropped region. Thus, correct order is aCropRect, aCurrentRect mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(), - rAttr.GetTransparency(), aCropRect, aCurrentRect); + 255 - rAttr.GetAlpha(), aCropRect, aCurrentRect); } void VclMetafileProcessor2D::processControlPrimitive2D( @@ -1090,7 +1193,7 @@ void VclMetafileProcessor2D::processControlPrimitive2D( uno::Reference<beans::XPropertySetInfo> xPropertyInfo( xModelProperties.is() ? xModelProperties->getPropertySetInfo() : uno::Reference<beans::XPropertySetInfo>()); - const OUString sPrintablePropertyName("Printable"); + static constexpr OUString sPrintablePropertyName(u"Printable"_ustr); if (xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName)) { @@ -1109,14 +1212,25 @@ void VclMetafileProcessor2D::processControlPrimitive2D( if (!bIsPrintableControl) return; + ::std::optional<sal_Int32> oAnchorParent; + if (mpPDFExtOutDevData) + { + if (rControlPrimitive.GetAnchorStructureElementKey()) + { + sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement( + rControlPrimitive.GetAnchorStructureElementKey()); + oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement()); + mpPDFExtOutDevData->SetCurrentStructureElement(id); + } + } + const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields()); bool bDoProcessRecursively(true); + bool bDecorative = (mpCurrentStructureTag && mpCurrentStructureTag->isDecorative()); - if (bPDFExport) + if (bPDFExport && !bDecorative) { // PDF export. Emulate data handling from UnoControlPDFExportContact - // I have now moved describePDFControl to toolkit, thus i can implement the PDF - // form control support now as follows std::unique_ptr<vcl::PDFWriter::AnyWidget> pPDFControl( ::toolkitform::describePDFControl(rXControl, *mpPDFExtOutDevData)); @@ -1136,9 +1250,37 @@ void VclMetafileProcessor2D::processControlPrimitive2D( mpOutputDevice->GetMapMode()); pPDFControl->TextFont.SetFontSize(aFontSize); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Form); + vcl::PDFWriter::StructAttributeValue role; + switch (pPDFControl->Type) + { + case vcl::PDFWriter::PushButton: + role = vcl::PDFWriter::Pb; + break; + case vcl::PDFWriter::RadioButton: + role = vcl::PDFWriter::Rb; + break; + case vcl::PDFWriter::CheckBox: + role = vcl::PDFWriter::Cb; + break; + default: // there is a paucity of roles, tv is the catch-all one + role = vcl::PDFWriter::Tv; + break; + } + // ISO 14289-1:2014, Clause: 7.18.4 + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Role, role); + // ISO 14289-1:2014, Clause: 7.18.1 + OUString const& rAltText(rControlPrimitive.GetAltText()); + if (!rAltText.isEmpty()) + { + mpPDFExtOutDevData->SetAlternateText(rAltText); + } mpPDFExtOutDevData->CreateControl(*pPDFControl); mpPDFExtOutDevData->EndStructureElement(); + if (oAnchorParent) + { + mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent); + } // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject); // do not process recursively @@ -1152,6 +1294,32 @@ void VclMetafileProcessor2D::processControlPrimitive2D( } } + if (!bDoProcessRecursively) + { + return; + } + + if (mpPDFExtOutDevData) + { // no corresponding PDF Form, use Figure instead + if (!bDecorative) + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Figure); + else + mpPDFExtOutDevData->WrapBeginStructureElement( + vcl::pdf::StructElement::NonStructElement); + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, vcl::PDFWriter::Block); + auto const range(rControlPrimitive.getB2DRange(getViewInformation2D())); + tools::Rectangle const aLogicRect(basegfx::fround<tools::Long>(range.getMinX()), + basegfx::fround<tools::Long>(range.getMinY()), + basegfx::fround<tools::Long>(range.getMaxX()), + basegfx::fround<tools::Long>(range.getMaxY())); + mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect); + OUString const& rAltText(rControlPrimitive.GetAltText()); + if (!rAltText.isEmpty() && !bDecorative) + { + mpPDFExtOutDevData->SetAlternateText(rAltText); + } + } + // #i93169# used flag the wrong way; true means that nothing was done yet if (bDoProcessRecursively) { @@ -1196,6 +1364,15 @@ void VclMetafileProcessor2D::processControlPrimitive2D( { process(rControlPrimitive); } + + if (mpPDFExtOutDevData) + { + mpPDFExtOutDevData->EndStructureElement(); + if (oAnchorParent) + { + mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent); + } + } } void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( @@ -1203,10 +1380,9 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( { // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to) // thus do the MetafileAction embedding stuff but just handle recursively. - const OString aCommentStringCommon("FIELD_SEQ_BEGIN"); - const OString aCommentStringPage("FIELD_SEQ_BEGIN;PageField"); - const OString aCommentStringEnd("FIELD_SEQ_END"); + static constexpr OString aCommentStringCommon("FIELD_SEQ_BEGIN"_ostr); OUString aURL; + const bool bIsExportTaggedPDF(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF()); switch (rFieldPrimitive.getType()) { @@ -1217,18 +1393,22 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( } case drawinglayer::primitive2d::FIELD_TYPE_PAGE: { - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage)); + mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_BEGIN;PageField"_ostr)); break; } case drawinglayer::primitive2d::FIELD_TYPE_URL: { - aURL = rFieldPrimitive.getValue("URL"); + aURL = rFieldPrimitive.getValue(u"URL"_ustr); if (!aURL.isEmpty()) { mpMetaFile->AddAction(new MetaCommentAction( aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()), 2 * aURL.getLength())); + + if (bIsExportTaggedPDF) + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Link, + u"Link"_ustr); } break; @@ -1241,7 +1421,7 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( process(rContent); // for the end comment the type is not relevant yet, they are all the same. Just add. - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd)); + mpMetaFile->AddAction(new MetaCommentAction("FIELD_SEQ_END"_ostr)); if (!(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType())) @@ -1254,43 +1434,47 @@ void VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D( static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); vcl::PDFExtOutDevBookmarkEntry aBookmark; - aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic); + OUString const altText(rFieldPrimitive.getValue(u"AltText"_ustr)); + aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic, altText); aBookmark.aBookmark = aURL; std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = mpPDFExtOutDevData->GetBookmarks(); rBookmarks.push_back(aBookmark); + + if (bIsExportTaggedPDF) + { + mpPDFExtOutDevData->SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation, + aBookmark.nLinkId); + mpPDFExtOutDevData->EndStructureElement(); + } } void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D( const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive) { - const OString aCommentString("XTEXT_EOL"); - // process recursively and add MetaFile comment process(rLinePrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOL"_ostr)); } void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D( const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive) { - // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The - // "XTEXT_EOC" is used, use here, too. - const OString aCommentString("XTEXT_EOC"); - // this is a part of list item, start LILabel ( = bullet) if (mbInListItem) { - maListElements.push(vcl::PDFWriter::LILabel); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LILabel); + maListElements.push(vcl::pdf::StructElement::LILabel); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::LILabel); } // process recursively and add MetaFile comment process(rBulletPrimitive); - mpMetaFile->AddAction(new MetaCommentAction(aCommentString)); + // in Outliner::StripBullet(), a MetafileComment for bullets is added, too. The + // "XTEXT_EOC" is used, use here, too. + mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"_ostr)); if (mbInListItem) { - if (maListElements.top() == vcl::PDFWriter::LILabel) + if (maListElements.top() == vcl::pdf::StructElement::LILabel) { maListElements.pop(); mpPDFExtOutDevData->EndStructureElement(); // end LILabel @@ -1302,7 +1486,7 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D( void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive) { - const OString aCommentString("XTEXT_EOP"); + static constexpr OString aCommentString("XTEXT_EOP"_ostr); static bool bSuppressPDFExtOutDevDataSupport(false); // loplugin:constvars:ignore if (nullptr == mpPDFExtOutDevData || bSuppressPDFExtOutDevDataSupport) @@ -1318,7 +1502,7 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( { // No Tagged PDF -> Dump as Paragraph // Emulate data handling from old ImpEditEngine::Paint - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Paragraph); // Process recursively and add MetaFile comment process(rParagraphPrimitive); @@ -1343,8 +1527,8 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( // increase List level for (sal_Int16 a(mnCurrentOutlineLevel); a != nNewOutlineLevel; ++a) { - maListElements.push(vcl::PDFWriter::List); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::List); + maListElements.push(vcl::pdf::StructElement::List); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::List); } } else // if(nNewOutlineLevel < mnCurrentOutlineLevel) @@ -1374,14 +1558,14 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( if (bDumpAsListItem) { // Dump as ListItem - maListElements.push(vcl::PDFWriter::ListItem); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::ListItem); + maListElements.push(vcl::pdf::StructElement::ListItem); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::ListItem); mbInListItem = true; } else { // Dump as Paragraph - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Paragraph); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Paragraph); } // Process recursively and add MetaFile comment @@ -1397,11 +1581,8 @@ void VclMetafileProcessor2D::processTextHierarchyParagraphPrimitive2D( void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D( const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive) { - const OString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN"); - const OString aCommentStringB("XTEXT_PAINTSHAPE_END"); - // add MetaFile comment, process recursively and add MetaFile comment - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA)); + mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_BEGIN"_ostr)); process(rBlockPrimitive); if (mnCurrentOutlineLevel >= 0) @@ -1413,7 +1594,7 @@ void VclMetafileProcessor2D::processTextHierarchyBlockPrimitive2D( } } - mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB)); + mpMetaFile->AddAction(new MetaCommentAction("XTEXT_PAINTSHAPE_END"_ostr)); } void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( @@ -1427,8 +1608,8 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( // bullet has been already processed, start LIBody if (mbInListItem && mbBulletPresent) { - maListElements.push(vcl::PDFWriter::LIBody); - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::LIBody); + maListElements.push(vcl::pdf::StructElement::LIBody); + mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::LIBody); } // directdraw of text simple portion; use default processing @@ -1442,13 +1623,22 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( // #i101169# if(pTextDecoratedCandidate) { + /* break iterator support + made static so it only needs to be fetched once, even with many single + constructed VclMetafileProcessor2D. It's still incarnated on demand, + but exists for OOo runtime now by purpose. + */ + static tools::DeleteOnDeinit<css::uno::Reference<css::i18n::XBreakIterator>> + gxBreakIterator; + // support for TEXT_ MetaFile actions only for decorated texts - if (!mxBreakIterator.is()) + if (!gxBreakIterator.get() || !gxBreakIterator.get()->get()) { - uno::Reference<uno::XComponentContext> xContext( + const uno::Reference<uno::XComponentContext>& xContext( ::comphelper::getProcessComponentContext()); - mxBreakIterator = i18n::BreakIterator::create(xContext); + gxBreakIterator.set(i18n::BreakIterator::create(xContext)); } + auto& rBreakIterator = *gxBreakIterator.get()->get(); const OUString& rTxt = rTextCandidate.getText(); const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength()); @@ -1459,16 +1649,16 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( const sal_Int32 nTextPosition(rTextCandidate.getTextPosition()); sal_Int32 nDone; - sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters( + sal_Int32 nNextCellBreak(rBreakIterator.nextCharacters( rTxt, nTextPosition, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone)); - css::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary( + css::i18n::Boundary nNextWordBoundary(rBreakIterator.getWordBoundary( rTxt, nTextPosition, rLocale, css::i18n::WordType::ANY_WORD, true)); sal_Int32 nNextSentenceBreak( - mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale)); - const OString aCommentStringA("XTEXT_EOC"); - const OString aCommentStringB("XTEXT_EOW"); - const OString aCommentStringC("XTEXT_EOS"); + rBreakIterator.endOfSentence(rTxt, nTextPosition, rLocale)); + static constexpr OStringLiteral aCommentStringA("XTEXT_EOC"); + static constexpr OStringLiteral aCommentStringB("XTEXT_EOW"); + static constexpr OStringLiteral aCommentStringC("XTEXT_EOS"); for (sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++) { @@ -1477,21 +1667,21 @@ void VclMetafileProcessor2D::processTextSimplePortionPrimitive2D( { mpMetaFile->AddAction( new MetaCommentAction(aCommentStringA, i - nTextPosition)); - nNextCellBreak = mxBreakIterator->nextCharacters( + nNextCellBreak = rBreakIterator.nextCharacters( rTxt, i, rLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone); } if (i == nNextWordBoundary.endPos) { mpMetaFile->AddAction( new MetaCommentAction(aCommentStringB, i - nTextPosition)); - nNextWordBoundary = mxBreakIterator->getWordBoundary( + nNextWordBoundary = rBreakIterator.getWordBoundary( rTxt, i + 1, rLocale, css::i18n::WordType::ANY_WORD, true); } if (i == nNextSentenceBreak) { mpMetaFile->AddAction( new MetaCommentAction(aCommentStringC, i - nTextPosition)); - nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale); + nNextSentenceBreak = rBreakIterator.endOfSentence(rTxt, i + 1, rLocale); } } } @@ -1510,9 +1700,11 @@ void VclMetafileProcessor2D::processPolygonHairlinePrimitive2D( basegfx::B2DPolygon aLeft, aRight; splitLinePolygon(rBasePolygon, aLeft, aRight); rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPLeft( - new primitive2d::PolygonHairlinePrimitive2D(aLeft, rHairlinePrimitive.getBColor())); + new primitive2d::PolygonHairlinePrimitive2D(std::move(aLeft), + rHairlinePrimitive.getBColor())); rtl::Reference<primitive2d::PolygonHairlinePrimitive2D> xPRight( - new primitive2d::PolygonHairlinePrimitive2D(aRight, rHairlinePrimitive.getBColor())); + new primitive2d::PolygonHairlinePrimitive2D(std::move(aRight), + rHairlinePrimitive.getBColor())); processBasePrimitive2D(*xPLeft); processBasePrimitive2D(*xPRight); @@ -1560,10 +1752,12 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D( basegfx::B2DPolygon aLeft, aRight; splitLinePolygon(rBasePolygon, aLeft, aRight); rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPLeft( - new primitive2d::PolygonStrokePrimitive2D(aLeft, rStrokePrimitive.getLineAttribute(), + new primitive2d::PolygonStrokePrimitive2D(std::move(aLeft), + rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute())); rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xPRight( - new primitive2d::PolygonStrokePrimitive2D(aRight, rStrokePrimitive.getLineAttribute(), + new primitive2d::PolygonStrokePrimitive2D(std::move(aRight), + rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute())); processBasePrimitive2D(*xPLeft); @@ -1571,7 +1765,8 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D( } else { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + auto popIt + = mpOutputDevice->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); // support SvtGraphicStroke MetaCommentAction std::unique_ptr<SvtGraphicStroke> pSvtGraphicStroke = impTryToCreateSvtGraphicStroke( @@ -1582,34 +1777,90 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D( const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute(); // create MetaPolyLineActions, but without LineStyle::Dash - if (basegfx::fTools::more(rLine.getWidth(), 0.0)) + if (rLine.getWidth() > 0.0) { const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute(); - basegfx::B2DPolyPolygon aHairLinePolyPolygon; - - if (0.0 == rStroke.getFullDotDashLen()) - { - aHairLinePolyPolygon.append(rBasePolygon); - } - else - { - basegfx::utils::applyLineDashing(rBasePolygon, rStroke.getDotDashArray(), - &aHairLinePolyPolygon, nullptr, - rStroke.getFullDotDashLen()); - } const basegfx::BColor aHairlineColor( maBColorModifierStack.getModifiedColor(rLine.getColor())); mpOutputDevice->SetLineColor(Color(aHairlineColor)); mpOutputDevice->SetFillColor(); - aHairLinePolyPolygon.transform(maCurrentTransformation); // use the transformed line width LineInfo aLineInfo(LineStyle::Solid, - basegfx::fround(getTransformedLineWidth(rLine.getWidth()))); + std::round(getTransformedLineWidth(rLine.getWidth()))); aLineInfo.SetLineJoin(rLine.getLineJoin()); aLineInfo.SetLineCap(rLine.getLineCap()); + basegfx::B2DPolyPolygon aHairLinePolyPolygon; + if (0.0 == rStroke.getFullDotDashLen()) + { + aHairLinePolyPolygon.append(rBasePolygon); + } + else + { + bool done = false; + const std::vector<double>& array = rStroke.getDotDashArray(); + // The dotdash array should generally have the form + // (<dashLen> <distance>)+ (<dotLen> <distance>)* + // (where (,),+ and * have their regex meaning). + // Find out what the lengths and their counts are. + if (!array.empty() && array.size() % 2 == 0) + { + double dashLen = array[0]; + double distance = array[1]; + int dashCount = 1; + double dotLen = 0; + int dotCount = 0; + size_t pos = 2; + while (pos + 2 <= array.size()) + { + if (array[pos] != dashLen || array[pos + 1] != distance) + break; + ++dashCount; + pos += 2; + } + if (pos + 2 <= array.size() && array[pos + 1] == distance) + { + dotLen = array[pos]; + ++dotCount; + pos += 2; + while (pos + 2 <= array.size()) + { + if (array[pos] != dotLen || array[pos + 1] != distance) + break; + ++dotCount; + pos += 2; + } + } + if (array.size() == pos) + { + aHairLinePolyPolygon.append(rBasePolygon); + // This will be used by setupStrokeAttributes() in cppcanvas. + aLineInfo.SetStyle(LineStyle::Dash); + aLineInfo.SetDashCount(dashCount); + aLineInfo.SetDashLen(getTransformedLineWidth(dashLen)); + aLineInfo.SetDistance(getTransformedLineWidth(distance)); + if (dotCount != 0) + { + aLineInfo.SetDotCount(dotCount); + aLineInfo.SetDotLen(getTransformedLineWidth(dotLen)); + } + done = true; + } + } + if (!done) + { + // LineInfo can hold only limited info about dashing, apply dashing manually + // if LineInfo cannot describe it. That should not happen though. + SAL_WARN("drawinglayer", "dotdash array cannot be converted to LineInfo"); + basegfx::utils::applyLineDashing(rBasePolygon, rStroke.getDotDashArray(), + &aHairLinePolyPolygon, nullptr, + rStroke.getFullDotDashLen()); + } + } + aHairLinePolyPolygon.transform(maCurrentTransformation); + for (sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++) { const basegfx::B2DPolygon& aCandidate(aHairLinePolyPolygon.getB2DPolygon(a)); @@ -1628,8 +1879,6 @@ void VclMetafileProcessor2D::processPolygonStrokePrimitive2D( } impEndSvtGraphicStroke(pSvtGraphicStroke.get()); - - mpOutputDevice->Pop(); } } @@ -1707,6 +1956,16 @@ void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D( // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon()); + if (!rBitmapCandidate.getDefinitionRange().isEmpty() + && aLocalPolyPolygon.getB2DRange() != rBitmapCandidate.getDefinitionRange()) + { + // The range which defines the bitmap fill is defined and different from the + // range of the defining geometry (e.g. used for FillStyle UseSlideBackground). + // This cannot be done calling vcl, thus use decomposition here directly + process(rBitmapCandidate); + return; + } + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); std::unique_ptr<SvtGraphicFill> pSvtGraphicFill; @@ -1750,10 +2009,11 @@ void VclMetafileProcessor2D::processPolyPolygonGraphicPrimitive2D( aTransform.matrix[5] = aTransformPosition.getY(); pSvtGraphicFill.reset(new SvtGraphicFill( - getFillPolyPolygon(aLocalPolyPolygon), Color(), 0.0, SvtGraphicFill::fillEvenOdd, - SvtGraphicFill::fillTexture, aTransform, rFillGraphicAttribute.getTiling(), - SvtGraphicFill::hatchSingle, Color(), SvtGraphicFill::GradientType::Linear, Color(), - Color(), 0, rFillGraphicAttribute.getGraphic())); + getFillPolyPolygon(aLocalPolyPolygon), Color(), rBitmapCandidate.getTransparency(), + SvtGraphicFill::fillEvenOdd, SvtGraphicFill::fillTexture, aTransform, + rFillGraphicAttribute.getTiling(), SvtGraphicFill::hatchSingle, Color(), + SvtGraphicFill::GradientType::Linear, Color(), Color(), 0, + rFillGraphicAttribute.getGraphic())); } // Do use decomposition; encapsulate with SvtGraphicFill @@ -1857,8 +2117,8 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D( aToolsPolyPolygon, Hatch(aHatchStyle, Color(maBColorModifierStack.getModifiedColor(rFillHatchAttribute.getColor())), - basegfx::fround(rFillHatchAttribute.getDistance()), - basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800))); + basegfx::fround<tools::Long>(rFillHatchAttribute.getDistance()), + Degree10(basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttribute.getAngle()))))); impEndSvtGraphicFill(pSvtGraphicFill.get()); } @@ -1866,42 +2126,164 @@ void VclMetafileProcessor2D::processPolyPolygonHatchPrimitive2D( void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate) { - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - - maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); - - if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) - { - // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. - // This is because VCL Gradient mechanism does *not* support to rotate the gradient - // with objects and this case is not expressible in a Metafile (and cannot be added - // since the FileFormats used, e.g. *.wmf, do not support it either). - // Such cases happen when a graphic object uses a Metafile as graphic information or - // a fill style definition uses a Metafile. In this cases the graphic content is - // rotated with the graphic or filled object; this is not supported by the target - // format of this conversion renderer - Metafiles. - // To solve this, not a Gradient is written, but the decomposition of this object - // is written to the Metafile. This is the PolyPolygons building the gradient fill. - // These will need more space and time, but the result will be as if the Gradient - // was rotated with the object. - // This mechanism is used by all exporters still not using Primitives (e.g. Print, - // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile - // transfers. One more reason to *change* these to primitives. - // BTW: One more example how useful the principles of primitives are; the decomposition - // is by definition a simpler, maybe more expensive representation of the same content. - process(rGradientCandidate); + // SDPR: Caution: metafile export cannot handle added TransparencyGradient + if (rGradientCandidate.hasAlphaGradient() || rGradientCandidate.hasTransparency()) + { + // if it has alpha added directly we need to use the decomposition. + // unfortunately VclMetafileProcessor2D does *not* support the + // primitive created in the decomposition, the FillGradientPrimitive2D. + // at the same time extra stuff like adding gradient info to the + // metafile (BGRAD_SEQ_BEGIN) only is done HERE. To solve that and to + // not add PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D now, create a temporary + // decomposition to again get a PolyPolygonGradientPrimitive2D, but + // *without* directly added alpha + primitive2d::Primitive2DReference aRetval(new primitive2d::PolyPolygonGradientPrimitive2D( + rGradientCandidate.getB2DPolyPolygon(), rGradientCandidate.getDefinitionRange(), + rGradientCandidate.getFillGradient())); + + if (rGradientCandidate.hasAlphaGradient()) + { + const basegfx::B2DRange aPolyPolygonRange( + rGradientCandidate.getB2DPolyPolygon().getB2DRange()); + primitive2d::Primitive2DContainer aAlpha{ new primitive2d::FillGradientPrimitive2D( + aPolyPolygonRange, rGradientCandidate.getDefinitionRange(), + rGradientCandidate.getAlphaGradient()) }; + + aRetval = new primitive2d::TransparencePrimitive2D( + primitive2d::Primitive2DContainer{ aRetval }, std::move(aAlpha)); + } + + if (rGradientCandidate.hasTransparency()) + { + aRetval = new primitive2d::UnifiedTransparencePrimitive2D( + primitive2d::Primitive2DContainer{ aRetval }, rGradientCandidate.getTransparency()); + } + + process(primitive2d::Primitive2DContainer{ aRetval }); return; } + bool useDecompose(false); + + if (!useDecompose) + { + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + + maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); + + // detect if transformation is rotated, sheared or mirrored in X and/or Y + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX) + || aScale.getX() < 0.0 || aScale.getY() < 0.0) + { + // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly. + // This is because VCL Gradient mechanism does *not* support to rotate the gradient + // with objects and this case is not expressible in a Metafile (and cannot be added + // since the FileFormats used, e.g. *.wmf, do not support it either). + // Such cases happen when a graphic object uses a Metafile as graphic information or + // a fill style definition uses a Metafile. In this cases the graphic content is + // rotated with the graphic or filled object; this is not supported by the target + // format of this conversion renderer - Metafiles. + // To solve this, not a Gradient is written, but the decomposition of this object + // is written to the Metafile. This is the PolyPolygons building the gradient fill. + // These will need more space and time, but the result will be as if the Gradient + // was rotated with the object. + // This mechanism is used by all exporters still not using Primitives (e.g. Print, + // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile + // transfers. One more reason to *change* these to primitives. + // BTW: One more example how useful the principles of primitives are; the decomposition + // is by definition a simpler, maybe more expensive representation of the same content. + useDecompose = true; + } + } + + // tdf#150551 for PDF export, use the decomposition for better gradient visualization + if (!useDecompose && nullptr != mpPDFExtOutDevData) + { + useDecompose = true; + } + basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon()); - if (aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) + if (!useDecompose && aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange()) { // the range which defines the gradient is different from the range of the // geometry (used for writer frames). This cannot be done calling vcl, thus use // decomposition here + useDecompose = true; + } + + const attribute::FillGradientAttribute& rFillGradient(rGradientCandidate.getFillGradient()); + + if (!useDecompose && rFillGradient.cannotBeHandledByVCL()) + { + // MCGR: if we have ColorStops, do not try to fallback to old VCL-Gradient, + // that will *not* be capable of representing this properly. Use the + // correct decomposition instead + useDecompose = true; + } + + if (useDecompose) + { + GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile()); + + // tdf#155479 only add 'BGRAD_SEQ_BEGIN' if SVG export + // SDPR: Caution: metafile export cannot handle added TransparencyGradient + if (nullptr != pMetaFile && pMetaFile->getSVG() && !rGradientCandidate.hasAlphaGradient()) + { + // write the color stops to a memory stream + SvMemoryStream aMemStm; + VersionCompatWrite aCompat(aMemStm, 1); + + const basegfx::BColorStops& rColorStops(rFillGradient.getColorStops()); + sal_uInt16 nTmp(sal::static_int_cast<sal_uInt16>(rColorStops.size())); + aMemStm.WriteUInt16(nTmp); + + for (auto const& rCand : rColorStops) + { + aMemStm.WriteDouble(rCand.getStopOffset()); + const basegfx::BColor& rColor(rCand.getStopColor()); + aMemStm.WriteDouble(rColor.getRed()); + aMemStm.WriteDouble(rColor.getGreen()); + aMemStm.WriteDouble(rColor.getBlue()); + } + + // Add a new MetaCommentAction section of type 'BGRAD_SEQ_BEGIN/BGRAD_SEQ_END' + // that is capable of holding the new color step information, plus the + // already used MetaActionType::GRADIENTEX. + // With that combination only places that know about that new BGRAD_SEQ_* will + // use it while all others will work on the created decomposition of the + // gradient for compatibility - which are single-color filled polygons + pMetaFile->AddAction(new MetaCommentAction( + "BGRAD_SEQ_BEGIN"_ostr, 0, static_cast<const sal_uInt8*>(aMemStm.GetData()), + aMemStm.TellEnd())); + + // create MetaActionType::GRADIENTEX + // NOTE: with the new BGRAD_SEQ_* we could use basegfx::B2DPolygon and + // basegfx::BGradient here directly, but may have to add streaming OPs + // for these, so for now just go with what we use all the time. The real + // work for improvement should not go to this 'compromise' but to a real + // re-work of the SVG export (or/and others) to no longer work on metafiles + // but on UNO API or primitives (whatever fits best to the specific export) + fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon); + Gradient aVCLGradient; + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false); + aLocalPolyPolygon.transform(maCurrentTransformation); + const tools::PolyPolygon aToolsPolyPolygon( + getFillPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(aLocalPolyPolygon))); + mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient); + } + + // use decompose to draw, will create PolyPolygon ColorFill actions process(rGradientCandidate); + + // tdf#155479 only add 'BGRAD_SEQ_END' if SVG export + if (nullptr != pMetaFile && pMetaFile->getSVG()) + { + // close the BGRAD_SEQ_* actions range + pMetaFile->AddAction(new MetaCommentAction("BGRAD_SEQ_END"_ostr)); + } + return; } @@ -1913,8 +2295,7 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( // it is safest to use the VCL OutputDevice::DrawGradient method which creates those. // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon Gradient aVCLGradient; - impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), - false); + impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rFillGradient, false); aLocalPolyPolygon.transform(maCurrentTransformation); // #i82145# ATM VCL printing of gradients using curved shapes does not work, @@ -1935,16 +2316,16 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( switch (aVCLGradient.GetStyle()) { - default: // GradientStyle::Linear: - case GradientStyle::Axial: + default: // css::awt::GradientStyle_LINEAR: + case css::awt::GradientStyle_AXIAL: eGrad = SvtGraphicFill::GradientType::Linear; break; - case GradientStyle::Radial: - case GradientStyle::Elliptical: + case css::awt::GradientStyle_RADIAL: + case css::awt::GradientStyle_ELLIPTICAL: eGrad = SvtGraphicFill::GradientType::Radial; break; - case GradientStyle::Square: - case GradientStyle::Rect: + case css::awt::GradientStyle_SQUARE: + case css::awt::GradientStyle_RECT: eGrad = SvtGraphicFill::GradientType::Rectangular; break; } @@ -1965,7 +2346,7 @@ void VclMetafileProcessor2D::processPolyPolygonGradientPrimitive2D( void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D( const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate) { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points @@ -1981,8 +2362,6 @@ void VclMetafileProcessor2D::processPolyPolygonColorPrimitive2D( mpOutputDevice->SetLineColor(); mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); - - mpOutputDevice->Pop(); } void VclMetafileProcessor2D::processMaskPrimitive2D( @@ -1996,6 +2375,12 @@ void VclMetafileProcessor2D::processMaskPrimitive2D( if (aMask.count()) { + // A clipping path is a set of closed vector path that may consist of an arbitrary number + // of straight and curved segments. The region(s) enclosed by the path define(s) the visible area, + // i.e. after applying a clipping path, only those portions of the subsequently drawn graphics that + // fall inside the enclosed area are visible, everything else is cut away. + if (!aMask.isClosed()) + aMask.setClosed(true); // prepare new mask polygon and rescue current one aMask.transform(maCurrentTransformation); const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon); @@ -2012,7 +2397,7 @@ void VclMetafileProcessor2D::processMaskPrimitive2D( else { // use mask directly - maClipPolyPolygon = aMask; + maClipPolyPolygon = std::move(aMask); } if (maClipPolyPolygon.count()) @@ -2020,16 +2405,13 @@ void VclMetafileProcessor2D::processMaskPrimitive2D( // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!) // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where // the ClipRegion is built from the Polygon. An AdaptiveSubdivide on the source polygon was missing there - mpOutputDevice->Push(PushFlags::CLIPREGION); + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); mpOutputDevice->SetClipRegion(vcl::Region(maClipPolyPolygon)); // recursively paint content // #i121267# Only need to process sub-content when clip polygon is *not* empty. // If it is empty, the clip is empty and there can be nothing inside. process(rMaskCandidate.getChildren()); - - // restore VCL clip region - mpOutputDevice->Pop(); } // restore to rescued clip polygon @@ -2045,7 +2427,7 @@ void VclMetafileProcessor2D::processMaskPrimitive2D( void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) { - mpOutputDevice->Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR); + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); // for metafile: Need to examine what the pure vcl version is doing here actually // - uses DrawTransparent with metafile for content and a gradient // - uses DrawTransparent for single PolyPolygons directly. Can be detected by @@ -2069,7 +2451,7 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( if (!bForceToMetafile && 1 == rContent.size()) { - const primitive2d::Primitive2DReference xReference(rContent[0]); + const primitive2d::Primitive2DReference& xReference(rContent.front()); pPoPoColor = dynamic_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( xReference.get()); } @@ -2113,6 +2495,11 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( // various content, create content-metafile GDIMetaFile aContentMetafile; + + // tdf#155479 always forward propagate SVG flag for sub-content, + // it may contain cannotBeHandledByVCL gradients or transparencyGradients + aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG()); + const tools::Rectangle aPrimitiveRectangle( impDumpToMetaFile(rContent, aContentMetafile)); @@ -2126,10 +2513,10 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0))); const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl); - aVCLGradient.SetStyle(GradientStyle::Linear); + aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR); aVCLGradient.SetStartColor(aTransColor); aVCLGradient.SetEndColor(aTransColor); - aVCLGradient.SetAngle(0); + aVCLGradient.SetAngle(0_deg10); aVCLGradient.SetBorder(0); aVCLGradient.SetOfsX(0); aVCLGradient.SetOfsY(0); @@ -2143,8 +2530,6 @@ void VclMetafileProcessor2D::processUnifiedTransparencePrimitive2D( } } } - - mpOutputDevice->Pop(); } void VclMetafileProcessor2D::processTransparencePrimitive2D( @@ -2156,29 +2541,82 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D( // FillGradientPrimitive2D and reconstruct the gradient. // If that detection goes wrong, I have to create a transparence-blended bitmap. Eventually // do that in stripes, else RenderTransparencePrimitive2D may just be used - const primitive2d::Primitive2DContainer& rContent = rTransparenceCandidate.getChildren(); - const primitive2d::Primitive2DContainer& rTransparence - = rTransparenceCandidate.getTransparence(); + const primitive2d::Primitive2DContainer& rContent(rTransparenceCandidate.getChildren()); + const primitive2d::Primitive2DContainer& rTransparence( + rTransparenceCandidate.getTransparence()); if (rContent.empty() || rTransparence.empty()) return; // try to identify a single FillGradientPrimitive2D in the - // transparence part of the primitive - const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr; + // transparence part of the primitive. The hope is to handle + // the more specific case in a better way than the general + // TransparencePrimitive2D which has strongly separated + // definitions for transparency and content, both completely + // free definable by primitives + const primitive2d::FillGradientPrimitive2D* pFiGradient(nullptr); static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore + // check for single FillGradientPrimitive2D if (!bForceToBigTransparentVDev && 1 == rTransparence.size()) { - const primitive2d::Primitive2DReference xReference(rTransparence[0]); - pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>(xReference.get()); + pFiGradient = dynamic_cast<const primitive2d::FillGradientPrimitive2D*>( + rTransparence.front().get()); + + // check also for correct ID to exclude derived implementations + if (pFiGradient + && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D != pFiGradient->getPrimitive2DID()) + pFiGradient = nullptr; } - // Check also for correct ID to exclude derived implementations - if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID()) + // tdf#155479 preps for holding extra-MCGR infos + bool bSVGTransparencyColorStops(false); + basegfx::BColorStops aSVGTransparencyColorStops; + + // MCGR: tdf#155437 If we have identified a transparency gradient, + // check if VCL is able to handle it at all + if (nullptr != pFiGradient && pFiGradient->getFillGradient().cannotBeHandledByVCL()) { - // various content, create content-metafile + // If not, reset the pointer and do not make use of this special case. + // Adding a gradient in incomplete state that can not be handled by vcl + // makes no sense and will knowingly lead to errors, especially with + // MCGR extended possibilities. I checked what happens with the + // MetaFloatTransparentAction added by OutputDevice::DrawTransparent, but + // in most cases it gets converted to bitmap or even ignored, see e.g. + // - vcl/source/gdi/pdfwriter_impl2.cxx for PDF export + // - vcl/source/filter/wmf/wmfwr.cxx -> does ignore TransparenceGradient completely + // - vcl/source/filter/wmf/emfwr.cxx -> same + // - vcl/source/filter/eps/eps.cxx -> same + // NOTE: Theoretically it would be possible to make the new extended Gradient data + // available in metafiles, with the known limitations (not backward comp, all + // places using it would need adaption, ...), but combined with knowing that nearly + // all usages ignore or render it locally anyways makes that a non-option. + + // tdf#155479 Yepp, as already mentioned above we need to add + // some MCGR infos in case of SVG export, prepare that here + if (mpOutputDevice->GetConnectMetaFile()->getSVG()) + { + // for SVG, do not use decompose & prep extra data + bSVGTransparencyColorStops = true; + aSVGTransparencyColorStops = pFiGradient->getFillGradient().getColorStops(); + } + else + { + // use decomposition + pFiGradient = nullptr; + } + } + + if (nullptr != pFiGradient) + { + // this combination of Gradient can be expressed/handled by + // vcl/metafile, so add it directly. various content, create content-metafile GDIMetaFile aContentMetafile; + + // tdf#155479 always forward propagate SVG flag for sub-content, + // it may contain cannotBeHandledByVCL gradients or transparencyGradients + aContentMetafile.setSVG(mpOutputDevice->GetConnectMetaFile()->getSVG()); + const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile)); // re-create a VCL-gradient from FillGradientPrimitive2D @@ -2186,127 +2624,193 @@ void VclMetafileProcessor2D::processTransparencePrimitive2D( impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true); - // render it to VCL - mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(), - aPrimitiveRectangle.GetSize(), aVCLGradient); - } - else - { - // sub-transparence group. Draw to VDev first. - // this may get refined to tiling when resolution is too big here - - // need to avoid switching off MapMode stuff here; maybe need another - // tooling class, cannot just do the same as with the pixel renderer. - // Need to experiment... - - // Okay, basic implementation finished and tested. The DPI stuff was hard - // and not easy to find out that it's needed. - // Since this will not yet happen normally (as long as no one constructs - // transparence primitives with non-trivial transparence content) i will for now not - // refine to tiling here. - - basegfx::B2DRange aViewRange(rContent.getB2DRange(getViewInformation2D())); - aViewRange.transform(maCurrentTransformation); - const tools::Rectangle aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())), - static_cast<sal_Int32>(floor(aViewRange.getMinY())), - static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), - static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); - const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic)); - Size aSizePixel(aRectPixel.GetSize()); - const Point aEmptyPoint; - ScopedVclPtrInstance<VirtualDevice> aBufferDevice; - const sal_uInt32 nMaxQuadratPixels(500000); - const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight()); - double fReduceFactor(1.0); - - if (nViewVisibleArea > nMaxQuadratPixels) - { - // reduce render size - fReduceFactor = sqrt(double(nMaxQuadratPixels) / static_cast<double>(nViewVisibleArea)); - aSizePixel = Size( - basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * fReduceFactor), - basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * fReduceFactor)); - } - - if (aBufferDevice->SetOutputSizePixel(aSizePixel)) - { - // create and set MapModes for target devices - MapMode aNewMapMode(mpOutputDevice->GetMapMode()); - aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top())); - aBufferDevice->SetMapMode(aNewMapMode); - - // prepare view transformation for target renderers - // ATTENTION! Need to apply another scaling because of the potential DPI differences - // between Printer and VDev (mpOutputDevice and aBufferDevice here). - // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used. - basegfx::B2DHomMatrix aViewTransform(aBufferDevice->GetViewTransformation()); - const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const double fDPIXChange(static_cast<double>(aDPIOld.getWidth()) - / static_cast<double>(aDPINew.getWidth())); - const double fDPIYChange(static_cast<double>(aDPIOld.getHeight()) - / static_cast<double>(aDPINew.getHeight())); - - if (!basegfx::fTools::equal(fDPIXChange, 1.0) - || !basegfx::fTools::equal(fDPIYChange, 1.0)) - { - aViewTransform.scale(fDPIXChange, fDPIYChange); - } - - // also take scaling from Size reduction into account - if (!basegfx::fTools::equal(fReduceFactor, 1.0)) - { - aViewTransform.scale(fReduceFactor, fReduceFactor); - } - - // create view information and pixel renderer. Reuse known ViewInformation - // except new transformation and range - const geometry::ViewInformation2D aViewInfo( - getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange, - getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - - VclPixelProcessor2D aBufferProcessor(aViewInfo, *aBufferDevice); - - // draw content using pixel renderer - aBufferProcessor.process(rContent); - const Bitmap aBmContent(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel)); - - // draw transparence using pixel renderer - aBufferDevice->Erase(); - aBufferProcessor.process(rTransparence); - const AlphaMask aBmAlpha(aBufferDevice->GetBitmap(aEmptyPoint, aSizePixel)); + if (bSVGTransparencyColorStops) + { + // tdf#155479 create action directly & add extra + // MCGR infos to the metafile, do that by adding - ONLY in + // case of SVG export - to the MetaFileAction. For that + // reason, do what OutputDevice::DrawTransparent will do, + // but locally. + // NOTE: That would be good for this whole + // VclMetafileProcessor2D anyways to allow to get it + // completely independent from OutputDevice in the long run + GDIMetaFile* pMetaFile(mpOutputDevice->GetConnectMetaFile()); + rtl::Reference<::MetaFloatTransparentAction> pAction(new MetaFloatTransparentAction( + aContentMetafile, aPrimitiveRectangle.TopLeft(), aPrimitiveRectangle.GetSize(), + std::move(aVCLGradient), std::move(aSVGTransparencyColorStops))); - // paint - mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), - BitmapEx(aBmContent, aBmAlpha)); + pMetaFile->AddAction(pAction); } + else + { + // render it to VCL (creates MetaFloatTransparentAction) + mpOutputDevice->DrawTransparent(aContentMetafile, aPrimitiveRectangle.TopLeft(), + aPrimitiveRectangle.GetSize(), aVCLGradient); + } + return; } + + // Here we need to create a correct replacement visualization for the + // TransparencePrimitive2D for the target metafile. + // I replaced the n'th iteration to convert-to-bitmap which was + // used here by using the existing tooling. The orig here was also producing + // transparency errors with test-file from tdf#155437 on the right part of the + // image. + // Just rely on existing tooling doing the right thing in one place, so also + // corrections/optimizations can be in one single place + + // Start by getting logic range of content, transform object-to-world, then world-to-view + // to get to discrete values ('pixels'). Matrix multiplication is right-to-left (and not + // commutative) + basegfx::B2DRange aLogicRange(rTransparenceCandidate.getB2DRange(getViewInformation2D())); + aLogicRange.transform(mpOutputDevice->GetViewTransformation() * maCurrentTransformation); + + // expand in discrete coordinates to next-bigger 'pixel' boundaries and remember + // created discrete range + aLogicRange.expand( + basegfx::B2DPoint(floor(aLogicRange.getMinX()), floor(aLogicRange.getMinY()))); + aLogicRange.expand(basegfx::B2DPoint(ceil(aLogicRange.getMaxX()), ceil(aLogicRange.getMaxY()))); + const basegfx::B2DRange aDiscreteRange(aLogicRange); + + // transform back from discrete to world coordinates: this creates the + // pixel-boundaries extended logic range we need to cover all content + // reliably + aLogicRange.transform(mpOutputDevice->GetInverseViewTransformation()); + + // create transform embedding for renderer. Goal is to translate what we + // want to paint to top/left 0/0 and the calculated discrete size + basegfx::B2DHomMatrix aEmbedding(basegfx::utils::createTranslateB2DHomMatrix( + -aLogicRange.getMinX(), -aLogicRange.getMinY())); + const double fLogicWidth( + basegfx::fTools::equalZero(aLogicRange.getWidth()) ? 1.0 : aLogicRange.getWidth()); + const double fLogicHeight( + basegfx::fTools::equalZero(aLogicRange.getHeight()) ? 1.0 : aLogicRange.getHeight()); + aEmbedding.scale(aDiscreteRange.getWidth() / fLogicWidth, + aDiscreteRange.getHeight() / fLogicHeight); + + // use the whole TransparencePrimitive2D as input (no need to create a new + // one with the sub-contents, these are ref-counted) and add to embedding + // primitive2d::TransparencePrimitive2D& rTrCand(); + primitive2d::Primitive2DContainer xEmbedSeq{ &const_cast<primitive2d::TransparencePrimitive2D&>( + rTransparenceCandidate) }; + + // tdf#158743 when embedding, do not forget to 1st apply the evtl. used + // CurrentTransformation (right-to-left, apply that 1st) + xEmbedSeq = primitive2d::Primitive2DContainer{ new primitive2d::TransformPrimitive2D( + aEmbedding * maCurrentTransformation, std::move(xEmbedSeq)) }; + + // use empty ViewInformation & a useful MaximumQuadraticPixels + // limitation to paint the content + const auto aViewInformation2D(geometry::createViewInformation2D({})); + const sal_uInt32 nMaximumQuadraticPixels(500000); + const Bitmap aBitmap(convertToBitmap( + std::move(xEmbedSeq), aViewInformation2D, basegfx::fround(aDiscreteRange.getWidth()), + basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels)); + + // add to target metafile (will create MetaFloatTransparentAction) + mpOutputDevice->DrawBitmapEx(Point(basegfx::fround<tools::Long>(aLogicRange.getMinX()), + basegfx::fround<tools::Long>(aLogicRange.getMinY())), + Size(basegfx::fround<tools::Long>(aLogicRange.getWidth()), + basegfx::fround<tools::Long>(aLogicRange.getHeight())), + aBitmap); } void VclMetafileProcessor2D::processStructureTagPrimitive2D( const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate) { + ::comphelper::ValueRestorationGuard const g(mpCurrentStructureTag, &rStructureTagCandidate); + // structured tag primitive - const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); - bool bTagUsed((vcl::PDFWriter::NonStructElement != rTagElement)); + const vcl::pdf::StructElement& rTagElement(rStructureTagCandidate.getStructureElement()); + bool bTagUsed((vcl::pdf::StructElement::NonStructElement != rTagElement)); + ::std::optional<sal_Int32> oAnchorParent; + + if (!rStructureTagCandidate.isTaggedSdrObject()) + { + bTagUsed = false; + } if (mpPDFExtOutDevData && bTagUsed) { // foreground object: tag as regular structure element if (!rStructureTagCandidate.isBackground()) { - mpPDFExtOutDevData->BeginStructureElement(rTagElement); + if (rStructureTagCandidate.GetAnchorStructureElementKey() != nullptr) + { + sal_Int32 const id = mpPDFExtOutDevData->EnsureStructureElement( + rStructureTagCandidate.GetAnchorStructureElementKey()); + oAnchorParent.emplace(mpPDFExtOutDevData->GetCurrentStructureElement()); + mpPDFExtOutDevData->SetCurrentStructureElement(id); + } + mpPDFExtOutDevData->WrapBeginStructureElement(rTagElement); + switch (rTagElement) + { + case vcl::pdf::StructElement::H1: + case vcl::pdf::StructElement::H2: + case vcl::pdf::StructElement::H3: + case vcl::pdf::StructElement::H4: + case vcl::pdf::StructElement::H5: + case vcl::pdf::StructElement::H6: + case vcl::pdf::StructElement::Paragraph: + case vcl::pdf::StructElement::Heading: + case vcl::pdf::StructElement::Title: + case vcl::pdf::StructElement::Caption: + case vcl::pdf::StructElement::BlockQuote: + case vcl::pdf::StructElement::Table: + case vcl::pdf::StructElement::TableRow: + case vcl::pdf::StructElement::Formula: + case vcl::pdf::StructElement::Figure: + case vcl::pdf::StructElement::Annot: + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, + vcl::PDFWriter::Block); + break; + case vcl::pdf::StructElement::TableData: + case vcl::pdf::StructElement::TableHeader: + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Placement, + vcl::PDFWriter::Inline); + break; + default: + break; + } + switch (rTagElement) + { + case vcl::pdf::StructElement::Table: + case vcl::pdf::StructElement::Formula: + case vcl::pdf::StructElement::Figure: + case vcl::pdf::StructElement::Annot: + { + auto const range(rStructureTagCandidate.getB2DRange(getViewInformation2D())); + tools::Rectangle const aLogicRect( + basegfx::fround<tools::Long>(range.getMinX()), + basegfx::fround<tools::Long>(range.getMinY()), + basegfx::fround<tools::Long>(range.getMaxX()), + basegfx::fround<tools::Long>(range.getMaxY())); + mpPDFExtOutDevData->SetStructureBoundingBox(aLogicRect); + break; + } + default: + break; + } + if (rTagElement == vcl::pdf::StructElement::Annot) + { + mpPDFExtOutDevData->SetStructureAnnotIds(rStructureTagCandidate.GetAnnotIds()); + } + if (rTagElement == vcl::pdf::StructElement::TableHeader) + { + mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope, + vcl::PDFWriter::Column); + } } // background object else { // background image: tag as artifact if (rStructureTagCandidate.isImage()) - mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::NonStructElement); + mpPDFExtOutDevData->WrapBeginStructureElement( + vcl::pdf::StructElement::NonStructElement); // any other background object: do not tag else - bTagUsed = false; + assert(false); } } @@ -2317,97 +2821,10 @@ void VclMetafileProcessor2D::processStructureTagPrimitive2D( { // write end tag mpPDFExtOutDevData->EndStructureElement(); - } -} - -VclPtr<VirtualDevice> -VclMetafileProcessor2D::CreateBufferDevice(const basegfx::B2DRange& rCandidateRange, - geometry::ViewInformation2D& rViewInfo, - tools::Rectangle& rRectLogic, Size& rSizePixel) -{ - constexpr double fMaxQuadratPixels = 500000; - basegfx::B2DRange aViewRange(rCandidateRange); - aViewRange.transform(maCurrentTransformation); - rRectLogic = tools::Rectangle(static_cast<long>(std::floor(aViewRange.getMinX())), - static_cast<long>(std::floor(aViewRange.getMinY())), - static_cast<long>(std::ceil(aViewRange.getMaxX())), - static_cast<long>(std::ceil(aViewRange.getMaxY()))); - const tools::Rectangle aRectPixel(mpOutputDevice->LogicToPixel(rRectLogic)); - rSizePixel = aRectPixel.GetSize(); - const double fViewVisibleArea(rSizePixel.getWidth() * rSizePixel.getHeight()); - double fReduceFactor(1.0); - - if (fViewVisibleArea > fMaxQuadratPixels) - { - // reduce render size - fReduceFactor = sqrt(fMaxQuadratPixels / fViewVisibleArea); - rSizePixel = Size(basegfx::fround(rSizePixel.getWidth() * fReduceFactor), - basegfx::fround(rSizePixel.getHeight() * fReduceFactor)); - } - - VclPtrInstance<VirtualDevice> pBufferDevice(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); - if (pBufferDevice->SetOutputSizePixel(rSizePixel)) - { - // create and set MapModes for target devices - MapMode aNewMapMode(mpOutputDevice->GetMapMode()); - aNewMapMode.SetOrigin(Point(-rRectLogic.Left(), -rRectLogic.Top())); - pBufferDevice->SetMapMode(aNewMapMode); - - // prepare view transformation for target renderers - // ATTENTION! Need to apply another scaling because of the potential DPI differences - // between Printer and VDev (mpOutputDevice and pBufferDevice here). - // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch needs to be used. - basegfx::B2DHomMatrix aViewTransform(pBufferDevice->GetViewTransformation()); - const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const Size aDPINew(pBufferDevice->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); - const double fDPIXChange(static_cast<double>(aDPIOld.getWidth()) - / static_cast<double>(aDPINew.getWidth())); - const double fDPIYChange(static_cast<double>(aDPIOld.getHeight()) - / static_cast<double>(aDPINew.getHeight())); - - if (!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0)) + if (oAnchorParent) { - aViewTransform.scale(fDPIXChange, fDPIYChange); + mpPDFExtOutDevData->SetCurrentStructureElement(*oAnchorParent); } - - // also take scaling from Size reduction into account - if (!basegfx::fTools::equal(fReduceFactor, 1.0)) - { - aViewTransform.scale(fReduceFactor, fReduceFactor); - } - - // create view information and pixel renderer. Reuse known ViewInformation - // except new transformation and range - rViewInfo = geometry::ViewInformation2D( - getViewInformation2D().getObjectTransformation(), aViewTransform, aViewRange, - getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - } - else - pBufferDevice.disposeAndClear(); - - return std::move(pBufferDevice); -} - -void VclMetafileProcessor2D::processPrimitive2DOnPixelProcessor( - const primitive2d::BasePrimitive2D& rCandidate) -{ - basegfx::B2DRange aViewRange(rCandidate.getB2DRange(getViewInformation2D())); - geometry::ViewInformation2D aViewInfo; - tools::Rectangle aRectLogic; - Size aSizePixel; - auto pBufferDevice(CreateBufferDevice(aViewRange, aViewInfo, aRectLogic, aSizePixel)); - if (pBufferDevice) - { - VclPixelProcessor2D aBufferProcessor(aViewInfo, *pBufferDevice, maBColorModifierStack); - - // draw content using pixel renderer - primitive2d::Primitive2DReference aRef( - &const_cast<primitive2d::BasePrimitive2D&>(rCandidate)); - aBufferProcessor.process({ aRef }); - const BitmapEx aBmContent(pBufferDevice->GetBitmapEx(Point(), aSizePixel)); - mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), aRectLogic.GetSize(), aBmContent); - pBufferDevice.disposeAndClear(); } } diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx index 67a79ca307cc..46d1cb2f1edf 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx @@ -25,6 +25,7 @@ #include <com/sun/star/i18n/XBreakIterator.hpp> #include <basegfx/polygon/b2dpolypolygon.hxx> #include <vcl/pdfextoutdevdata.hxx> // vcl::PDFExtOutDevData support +#include <tools/lazydelete.hxx> class GDIMetaFile; namespace tools @@ -63,6 +64,8 @@ class PolyPolygonColorPrimitive2D; class MaskPrimitive2D; class UnifiedTransparencePrimitive2D; class TransparencePrimitive2D; +class ObjectInfoPrimitive2D; +class FillGraphicPrimitive2D; class StructureTagPrimitive2D; } @@ -105,7 +108,7 @@ private: const attribute::LineStartEndAttribute* pEnd); void impStartSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke); void impEndSvtGraphicStroke(SvtGraphicStroke const* pSvtGraphicStroke); - void popStructureElement(vcl::PDFWriter::StructElement eElem); + void popStructureElement(vcl::pdf::StructElement eElem); void popListItem(); void popList(); @@ -142,12 +145,12 @@ private: const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate); void processTransparencePrimitive2D( const primitive2d::TransparencePrimitive2D& rTransparenceCandidate); + void + processObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D); + void processFillGraphicPrimitive2D( + primitive2d::FillGraphicPrimitive2D const& rFillGraphicPrimitive2D); void processStructureTagPrimitive2D( const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate); - void processPrimitive2DOnPixelProcessor(const primitive2d::BasePrimitive2D& rCandidate); - VclPtr<VirtualDevice> CreateBufferDevice(const basegfx::B2DRange& rCandidateRange, - geometry::ViewInformation2D& rViewInfo, - tools::Rectangle& rRectLogic, Size& rSizePixel); /// Convert the fWidth to the same space as its coordinates. double getTransformedLineWidth(double fWidth) const; @@ -171,13 +174,6 @@ private: */ double mfCurrentUnifiedTransparence; - /* break iterator support - made static so it only needs to be fetched once, even with many single - constructed VclMetafileProcessor2D. It's still incarnated on demand, - but exists for OOo runtime now by purpose. - */ - static css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator; - /* vcl::PDFExtOutDevData support For the first step, some extra actions at vcl::PDFExtOutDevData need to be emulated with the VclMetafileProcessor2D. These are potentially temporarily @@ -193,7 +189,9 @@ private: bool mbInListItem; bool mbBulletPresent; - std::stack<vcl::PDFWriter::StructElement> maListElements; + std::stack<vcl::pdf::StructElement> maListElements; + + primitive2d::StructureTagPrimitive2D const* mpCurrentStructureTag = nullptr; protected: /* the local processor for BasePrimitive2D-Implementation based primitives, diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index b91bf57c360d..90dd5c5fb420 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -19,29 +19,24 @@ #include "vclpixelprocessor2d.hxx" #include "vclhelperbufferdevice.hxx" -#include "helperwrongspellrenderer.hxx" +#include <comphelper/lok.hxx> #include <sal/log.hxx> -#include <tools/stream.hxx> -#include <vcl/BitmapBasicMorphologyFilter.hxx> -#include <vcl/BitmapFilterStackBlur.hxx> #include <vcl/outdev.hxx> -#include <vcl/dibtools.hxx> #include <vcl/hatch.hxx> +#include <vcl/canvastools.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/Tools.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> @@ -50,9 +45,9 @@ #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> #include <drawinglayer/primitive2d/glowprimitive2d.hxx> -#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> #include <drawinglayer/primitive2d/controlprimitive2d.hxx> #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> #include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> @@ -60,49 +55,43 @@ #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> #include <drawinglayer/primitive2d/epsprimitive2d.hxx> -#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/patternfillprimitive2d.hxx> #include <com/sun/star/awt/XWindow2.hpp> #include <com/sun/star/awt/XControl.hpp> +#include <officecfg/Office/Common.hxx> +#include <vcl/gradient.hxx> + using namespace com::sun::star; namespace drawinglayer::processor2d { -struct VclPixelProcessor2D::Impl -{ - AntialiasingFlags m_nOrigAntiAliasing; - - explicit Impl(OutputDevice const& rOutDev) - : m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) - { - } -}; - VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, - OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack) - : VclProcessor2D(rViewInformation, rOutDev, rInitStack) - , m_pImpl(new Impl(rOutDev)) + OutputDevice& rOutDev) + : VclProcessor2D(rViewInformation, rOutDev) + , m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) + , m_bRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , m_bRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) { // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); // prepare output directly to pixels - mpOutputDevice->Push(PushFlags::MAPMODE); + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); mpOutputDevice->SetMapMode(); // react on AntiAliasing settings - if (getOptionsDrawinglayer().IsAntiAliasing()) + if (rViewInformation.getUseAntiAliasing()) { - mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing - | AntialiasingFlags::EnableB2dDraw); + mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing | AntialiasingFlags::Enable); } else { - mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing - & ~AntialiasingFlags::EnableB2dDraw); + mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing & ~AntialiasingFlags::Enable); } } @@ -112,7 +101,7 @@ VclPixelProcessor2D::~VclPixelProcessor2D() mpOutputDevice->Pop(); // restore AntiAliasing - mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing); + mpOutputDevice->SetAntialiasing(m_nOrigAntiAliasing); } void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect( @@ -127,7 +116,11 @@ void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect( const basegfx::BColor aPolygonColor( maBColorModifierStack.getModifiedColor(rSource.getBColor())); - mpOutputDevice->SetFillColor(Color(aPolygonColor)); + if (comphelper::LibreOfficeKit::isActive() && aPolygonColor.isAutomatic()) + mpOutputDevice->SetFillColor(getViewInformation2D().getAutoColor()); + else + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(), fTransparency); @@ -194,20 +187,13 @@ bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect( rSource.getLineAttribute().getWidth(), fTransparency, bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(), rSource.getLineAttribute().getLineJoin(), rSource.getLineAttribute().getLineCap(), - rSource.getLineAttribute().getMiterMinimumAngle() - /* false bBypassAACheck, default*/); + rSource.getLineAttribute().getMiterMinimumAngle()); } void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { switch (rCandidate.getPrimitive2DID()) { - case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D: - { - processWrongSpellPrimitive2D( - static_cast<const primitive2d::WrongSpellPrimitive2D&>(rCandidate)); - break; - } case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: { processTextSimplePortionPrimitive2D( @@ -228,7 +214,7 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv } case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: { - // direct draw of transformed BitmapEx primitive + // direct draw of transformed Bitmap primitive processBitmapPrimitive2D( static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); break; @@ -343,17 +329,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); break; } - case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D: - { - // #i97628# - // This primitive means that the content is derived from an active text edit, - // not from model data itself. Some renderers need to suppress this content, e.g. - // the pixel renderer used for displaying the edit view (like this one). It's - // not to be suppressed by the MetaFile renderers, so that the edited text is - // part of the MetaFile, e.g. needed for presentation previews. - // Action: Ignore here, do nothing. - break; - } case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: { processInvertPrimitive2D(rCandidate); @@ -382,22 +357,16 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); break; } - case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: - { - processGlowPrimitive2D( - static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D: + case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D: { - processSoftEdgePrimitive2D( - static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate)); + processFillGradientPrimitive2D( + static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D&>(rCandidate)); break; } - case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D: + case PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D: { - processShadowPrimitive2D( - static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate)); + processPatternFillPrimitive2D( + static_cast<const drawinglayer::primitive2d::PatternFillPrimitive2D&>(rCandidate)); break; } default: @@ -411,17 +380,6 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv } } -void VclPixelProcessor2D::processWrongSpellPrimitive2D( - const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive) -{ - if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive, *mpOutputDevice, maCurrentTransformation, - maBColorModifierStack)) - { - // fallback to decomposition (MetaFile) - process(rWrongSpellPrimitive); - } -} - void VclPixelProcessor2D::processTextSimplePortionPrimitive2D( const primitive2d::TextSimplePortionPrimitive2D& rCandidate) { @@ -429,7 +387,7 @@ void VclPixelProcessor2D::processTextSimplePortionPrimitive2D( const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); adaptTextToFillDrawMode(); - if (getOptionsDrawinglayer().IsRenderSimpleTextDirect()) + if (SAL_LIKELY(m_bRenderSimpleTextDirect)) { RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); } @@ -449,7 +407,7 @@ void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D( const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); adaptTextToFillDrawMode(); - if (getOptionsDrawinglayer().IsRenderDecoratedTextDirect()) + if (SAL_LIKELY(m_bRenderDecoratedTextDirect)) { RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); } @@ -501,15 +459,31 @@ void VclPixelProcessor2D::processBitmapPrimitive2D( void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) { - // direct draw of gradient - const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); - basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); - basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + // no geometry, no need to render, done if (!aLocalPolyPolygon.count()) return; + // *try* direct draw (AKA using old VCL stuff) to render gradient + const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); + + // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL + // so use decomposition + // NOTE: There may be even more reasons to detect, e.g. a ViewTransformation + // that uses shear/rotate/mirror (what VCL cannot handle at all), see + // other checks already in processFillGradientPrimitive2D + if (rGradient.cannotBeHandledByVCL()) + { + process(rPolygonCandidate); + return; + } + + basegfx::BColor aStartColor( + maBColorModifierStack.getModifiedColor(rGradient.getColorStops().front().getStopColor())); + basegfx::BColor aEndColor( + maBColorModifierStack.getModifiedColor(rGradient.getColorStops().back().getStopColor())); + if (aStartColor == aEndColor) { // no gradient at all, draw as polygon in AA and non-AA case @@ -517,12 +491,11 @@ void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( mpOutputDevice->SetLineColor(); mpOutputDevice->SetFillColor(Color(aStartColor)); mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + return; } - else - { - // use the primitive decomposition of the metafile - process(rPolygonCandidate); - } + + // use the primitive decomposition + process(rPolygonCandidate); } void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D( @@ -537,8 +510,8 @@ void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D( // when AA is on and this filled polygons are the result of stroked line geometry, // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons // Caution: This is needed in both cases (!) - if (!(mnPolygonStrokePrimitive2D && getOptionsDrawinglayer().IsAntiAliasing() - && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::EnableB2dDraw))) + if (!(mnPolygonStrokePrimitive2D && getViewInformation2D().getUseAntiAliasing() + && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::Enable))) return; const basegfx::BColor aPolygonColor( @@ -583,67 +556,60 @@ void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( if (1 == rContent.size()) { - const primitive2d::Primitive2DReference xReference(rContent[0]); - const primitive2d::BasePrimitive2D* pBasePrimitive - = dynamic_cast<const primitive2d::BasePrimitive2D*>(xReference.get()); + const primitive2d::BasePrimitive2D* pBasePrimitive = rContent.front().get(); - if (pBasePrimitive) + switch (pBasePrimitive->getPrimitive2DID()) { - switch (pBasePrimitive->getPrimitive2DID()) + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: { - case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: - { - // single transparent tools::PolyPolygon identified, use directly - const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor - = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( - pBasePrimitive); - SAL_WARN_IF(!pPoPoColor, "drawinglayer", - "OOps, PrimitiveID and PrimitiveType do not match (!)"); - bDrawTransparentUsed = true; - tryDrawPolyPolygonColorPrimitive2DDirect( - *pPoPoColor, rUniTransparenceCandidate.getTransparence()); - break; - } - case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: - { - // single transparent PolygonHairlinePrimitive2D identified, use directly - const primitive2d::PolygonHairlinePrimitive2D* pPoHair - = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>( - pBasePrimitive); - SAL_WARN_IF(!pPoHair, "drawinglayer", - "OOps, PrimitiveID and PrimitiveType do not match (!)"); - - // do no tallow by default - problem is that self-overlapping parts of this geometry will - // not be in an all-same transparency but will already alpha-cover themselves with blending. - // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its - // content to be uniformly transparent. - // For hairline the effect is pretty minimal, but still not correct. - bDrawTransparentUsed = false; - break; - } - case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: - { - // single transparent PolygonStrokePrimitive2D identified, use directly - const primitive2d::PolygonStrokePrimitive2D* pPoStroke - = static_cast<const primitive2d::PolygonStrokePrimitive2D*>( - pBasePrimitive); - SAL_WARN_IF(!pPoStroke, "drawinglayer", - "OOps, PrimitiveID and PrimitiveType do not match (!)"); - - // do no tallow by default - problem is that self-overlapping parts of this geometry will - // not be in an all-same transparency but will already alpha-cover themselves with blending. - // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its - // content to be uniformly transparent. - // To check, activate and draw a wide transparent self-crossing line/curve - bDrawTransparentUsed = false; - break; - } - default: - SAL_INFO("drawinglayer", - "default case for " << drawinglayer::primitive2d::idToString( - rUniTransparenceCandidate.getPrimitive2DID())); - break; + // single transparent tools::PolyPolygon identified, use directly + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor + = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( + pBasePrimitive); + assert(pPoPoColor && "OOps, PrimitiveID and PrimitiveType do not match (!)"); + bDrawTransparentUsed = true; + tryDrawPolyPolygonColorPrimitive2DDirect( + *pPoPoColor, rUniTransparenceCandidate.getTransparence()); + break; } + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: + { + // single transparent PolygonHairlinePrimitive2D identified, use directly + const primitive2d::PolygonHairlinePrimitive2D* pPoHair + = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>( + pBasePrimitive); + SAL_WARN_IF(!pPoHair, "drawinglayer", + "OOps, PrimitiveID and PrimitiveType do not match (!)"); + + // do no tallow by default - problem is that self-overlapping parts of this geometry will + // not be in an all-same transparency but will already alpha-cover themselves with blending. + // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its + // content to be uniformly transparent. + // For hairline the effect is pretty minimal, but still not correct. + bDrawTransparentUsed = false; + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + // single transparent PolygonStrokePrimitive2D identified, use directly + const primitive2d::PolygonStrokePrimitive2D* pPoStroke + = static_cast<const primitive2d::PolygonStrokePrimitive2D*>(pBasePrimitive); + SAL_WARN_IF(!pPoStroke, "drawinglayer", + "OOps, PrimitiveID and PrimitiveType do not match (!)"); + + // do no tallow by default - problem is that self-overlapping parts of this geometry will + // not be in an all-same transparency but will already alpha-cover themselves with blending. + // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its + // content to be uniformly transparent. + // To check, activate and draw a wide transparent self-crossing line/curve + bDrawTransparentUsed = false; + break; + } + default: + SAL_INFO("drawinglayer", + "default case for " << drawinglayer::primitive2d::idToString( + rUniTransparenceCandidate.getPrimitive2DID())); + break; } } @@ -658,43 +624,63 @@ void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( void VclPixelProcessor2D::processControlPrimitive2D( const primitive2d::ControlPrimitive2D& rControlPrimitive) { - // control primitive - const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); + // find out if the control is already visualized as a VCL-ChildWindow + bool bControlIsVisibleAsChildWindow(rControlPrimitive.isVisibleAsChildWindow()); + + // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple + // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the + // office is in non-layout mode (default for controls at startup). For the common office + // this means that there exists a real VCL-System-Window for the control, so it is *not* + // painted here due to being exactly obscured by that real Window (and creates danger of + // flickering, too). + // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but + // exactly that would be needed on each client displaying the tiles (what would be hard + // to do but also would have advantages - the clients would have real controls in the + // shape of their target system which could be interacted with...). It is also what the + // office does. + // For now, fallback to just render these controls when Tiled Rendering is active to just + // have them displayed on all clients. + if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) + { + // Do force paint when we are in Tiled Renderer and FormControl is 'visible' + bControlIsVisibleAsChildWindow = false; + } + + if (bControlIsVisibleAsChildWindow) + { + // f the control is already visualized as a VCL-ChildWindow it + // does not need to be painted at all + return; + } + + bool bDone(false); try { - // remember old graphics and create new - uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); - const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); - const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics()); + const uno::Reference<awt::XGraphics> xTargetGraphics(mpOutputDevice->CreateUnoGraphics()); - if (xNewGraphics.is()) + if (xTargetGraphics.is()) { - // link graphics and view - xControlView->setGraphics(xNewGraphics); + // Needs to be drawn. Link new graphics and view + const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); + uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); + xControlView->setGraphics(xTargetGraphics); // get position const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation * rControlPrimitive.getTransform()); const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); - // find out if the control is already visualized as a VCL-ChildWindow. If yes, - // it does not need to be painted at all. - uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW); - const bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() - && xControlWindow->isVisible()); - - if (!bControlIsVisibleAsChildWindow) - { - // draw it. Do not forget to use the evtl. offsetted origin of the target device, - // e.g. when used with mask/transparence buffer device - const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); - xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), - aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); - } + // Do not forget to use the evtl. offsetted origin of the target device, + // e.g. when used with mask/transparence buffer device + const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); + xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), + aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); // restore original graphics xControlView->setGraphics(xOriginalGraphics); + bDone = true; } } catch (const uno::Exception&) @@ -702,7 +688,10 @@ void VclPixelProcessor2D::processControlPrimitive2D( // #i116763# removing since there is a good alternative when the xControlView // is not found and it is allowed to happen // DBG_UNHANDLED_EXCEPTION(); + } + if (!bDone) + { // process recursively and use the decomposition as Bitmap process(rControlPrimitive); } @@ -739,7 +728,7 @@ void VclPixelProcessor2D::processPolygonStrokePrimitive2D( void VclPixelProcessor2D::processFillHatchPrimitive2D( const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive) { - if (getOptionsDrawinglayer().IsAntiAliasing()) + if (getViewInformation2D().getUseAntiAliasing()) { // if AA is used (or ignore smoothing is on), there is no need to smooth // hatch painting, use decomposition @@ -801,9 +790,10 @@ void VclPixelProcessor2D::processFillHatchPrimitive2D( const basegfx::B2DVector aDiscreteDistance( maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); - const sal_uInt16 nAngle10( - static_cast<sal_uInt16>(basegfx::fround(rFillHatchAttributes.getAngle() / F_PI1800))); - ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, nAngle10); + const sal_uInt32 nAngle10( + basegfx::fround(basegfx::rad2deg<10>(rFillHatchAttributes.getAngle()))); + ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, + Degree10(nAngle10)); // draw hatch using VCL mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch); @@ -817,14 +807,13 @@ void VclPixelProcessor2D::processBackgroundColorPrimitive2D( const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing()); // switch AA off in all cases - mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() - & ~AntialiasingFlags::EnableB2dDraw); + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~AntialiasingFlags::Enable); // create color for fill const basegfx::BColor aPolygonColor( maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); Color aFillColor(aPolygonColor); - aFillColor.SetTransparency(sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); + aFillColor.SetAlpha(255 - sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); mpOutputDevice->SetFillColor(aFillColor); mpOutputDevice->SetLineColor(); @@ -875,7 +864,7 @@ void VclPixelProcessor2D::processBorderLinePrimitive2D( && rBorder.isHorizontalOrVertical(getViewInformation2D())) { AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); - mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable); process(rBorder); mpOutputDevice->SetAntialiasing(nAntiAliasing); } @@ -890,24 +879,22 @@ void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimit // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.) // Set OutDev to XOR and switch AA off (XOR does not work with AA) - mpOutputDevice->Push(); + auto popIt = mpOutputDevice->ScopedPush(); mpOutputDevice->SetRasterOp(RasterOp::Xor); const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing()); - mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::Enable); // process content recursively process(rCandidate); // restore OutDev - mpOutputDevice->Pop(); mpOutputDevice->SetAntialiasing(nAntiAliasing); } void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { // #i98289# - const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() - && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); + const bool bForceLineSnap(getViewInformation2D().getPixelSnapHairline()); const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing()); if (bForceLineSnap) @@ -923,202 +910,222 @@ void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrim } } -namespace +void VclPixelProcessor2D::processFillGradientPrimitive2D( + const primitive2d::FillGradientPrimitive2D& rPrimitive) { -/* Returns 8-bit alpha mask created from passed mask. - - Negative fErodeDilateRadius values mean erode, positive - dilate. - nTransparency defines minimal transparency level. -*/ -AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius, - double fBlurRadius, sal_uInt8 nTransparency) -{ - // Only completely white pixels on the initial mask must be considered for transparency. Any - // other color must be treated as black. This creates 1-bit B&W bitmap. - BitmapEx mask(rMask.CreateMask(COL_WHITE)); - - // Scaling down increases performance without noticeable quality loss. Additionally, - // current blur implementation can only handle blur radius between 2 and 254. - Size aSize = mask.GetSizePixel(); - double fScale = 1.0; - while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000) + if (rPrimitive.hasAlphaGradient() || rPrimitive.hasTransparency()) { - fScale /= 2; - fBlurRadius /= 2; - fErodeDilateRadius /= 2; - aSize.setHeight(aSize.Height() / 2); - aSize.setWidth(aSize.Width() / 2); + // SDPR: As long as direct alpha is not supported by this + // renderer we need to work on the decomposition, so call it + process(rPrimitive); + return; } - // BmpScaleFlag::Fast is important for following color replacement - mask.Scale(fScale, fScale, BmpScaleFlag::Fast); + const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient(); + bool useDecompose(false); - if (fErodeDilateRadius > 0) - BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius)); - else if (fErodeDilateRadius < 0) - BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF)); - - if (nTransparency) + // MCGR: *many* - and not only GradientStops - cases cannot be handled by VCL + // so use decomposition + if (rFillGradient.cannotBeHandledByVCL()) { - const Color aTransparency(nTransparency, nTransparency, nTransparency); - mask.Replace(COL_BLACK, aTransparency); + useDecompose = true; } - // We need 8-bit grey mask for blurring - mask.Convert(BmpConversion::N8BitGreys); - - // calculate blurry effect - BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius)); - - mask.Scale(rMask.GetSizePixel()); - - return AlphaMask(mask.GetBitmap()); -} -} - -void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate) -{ - basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - aRange.transform(maCurrentTransformation); - basegfx::B2DVector aGlowRadiusVector(rCandidate.getGlowRadius(), 0); - // Calculate the pixel size of glow radius in current transformation - aGlowRadiusVector *= maCurrentTransformation; - // Glow radius is the size of the halo from each side of the object. The halo is the - // border of glow color that fades from glow transparency level to fully transparent - // When blurring a sharp boundary (our case), it gets 50% of original intensity, and - // fades to both sides by the blur radius; thus blur radius is half of glow radius. - const double fBlurRadius = aGlowRadiusVector.getLength() / 2; - // Consider glow transparency (initial transparency near the object edge) - const sal_uInt8 nTransparency = rCandidate.getGlowColor().GetTransparency(); - - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); - if (aBufferDevice.isVisible()) + // tdf#149754 VCL gradient draw is not capable to handle all primitive gradient definitions, + // what should be clear due to being developed to extend/replace them in + // capabilities & precision. + // It is e.g. not capable to correctly paint if the OutputRange is not completely + // inside the DefinitionRange, thus forcing to paint gradient parts *outside* the + // DefinitionRange. + // This happens for Writer with Frames anchored in Frames (and was broken due to + // falling back to VCL Gradient paint here), and for the new SlideBackgroundFill + // Fill mode for objects using it that reach outside the page (which is the + // DefinitionRange in that case). + // I see no real reason to fallback here to OutputDevice::DrawGradient and VCL + // gradient paint at all (system-dependent renderers wouldn't in the future), but + // will for convenience only add that needed additional correcting case + if (!useDecompose && !rPrimitive.getDefinitionRange().isInside(rPrimitive.getOutputRange())) { - // remember last OutDev and set to content - OutputDevice* pLastOutputDevice = mpOutputDevice; - mpOutputDevice = &aBufferDevice.getContent(); - // We don't need antialiased mask here, which would only make effect thicker - const auto aPrevAA = mpOutputDevice->GetAntialiasing(); - mpOutputDevice->SetAntialiasing(AntialiasingFlags::NONE); - mpOutputDevice->Erase(); - process(rCandidate); - const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), - static_cast<long>(std::floor(aRange.getMinY())), - static_cast<long>(std::ceil(aRange.getMaxX())), - static_cast<long>(std::ceil(aRange.getMaxY()))); - BitmapEx bmpEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); - mpOutputDevice->SetAntialiasing(aPrevAA); - - AlphaMask mask - = ProcessAndBlurAlphaMask(bmpEx.GetAlpha(), fBlurRadius, fBlurRadius, nTransparency); - - // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask - const basegfx::BColor aGlowColor( - maBColorModifierStack.getModifiedColor(rCandidate.getGlowColor().getBColor())); - Bitmap bmp = bmpEx.GetBitmap(); - bmp.Erase(Color(aGlowColor)); - BitmapEx result(bmp, mask); - - // back to old OutDev - mpOutputDevice = pLastOutputDevice; - mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); + useDecompose = true; } - else - SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); -} -void VclPixelProcessor2D::processSoftEdgePrimitive2D( - const primitive2d::SoftEdgePrimitive2D& rCandidate) -{ - // TODO: don't limit the object at view range. This is needed to not blur objects at window - // borders, where they don't end. Ideally, process the full object once at maximal reasonable - // resolution, and store the resulting alpha mask in primitive's cache; then reuse it later, - // applying the transform. - basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - aRange.transform(maCurrentTransformation); - basegfx::B2DVector aRadiusVector(rCandidate.getRadius(), 0); - // Calculate the pixel size of soft edge radius in current transformation - aRadiusVector *= maCurrentTransformation; - // Blur radius is equal to soft edge radius - const double fBlurRadius = aRadiusVector.getLength(); - - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); - if (aBufferDevice.isVisible()) + // tdf#151081 need to use regular primitive decomposition when the gradient + // is transformed in any other way then just translate & scale + if (!useDecompose) { - // remember last OutDev and set to content - OutputDevice* pLastOutputDevice = mpOutputDevice; - mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); - // Since the effect converts all children to bitmap, we can't disable antialiasing here, - // because it would result in poor quality in areas not affected by the effect - process(rCandidate); + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; - const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), - static_cast<long>(std::floor(aRange.getMinY())), - static_cast<long>(std::ceil(aRange.getMaxX())), - static_cast<long>(std::ceil(aRange.getMaxY()))); - BitmapEx bitmap = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); + maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX); - AlphaMask aMask = bitmap.GetAlpha(); - AlphaMask blurMask = ProcessAndBlurAlphaMask(aMask, -fBlurRadius, fBlurRadius, 0); + // detect if transformation is rotated, sheared or mirrored in X and/or Y + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX) + || aScale.getX() < 0.0 || aScale.getY() < 0.0) + { + useDecompose = true; + } + } - aMask.BlendWith(blurMask); + if (useDecompose) + { + // default is to use the direct render below. For security, + // keep the (simple) fallback to decompose in place here + static bool bTryDirectRender(true); - // The end result is the original bitmap with blurred 8-bit alpha mask - BitmapEx result(bitmap.GetBitmap(), aMask); + if (bTryDirectRender) + { + // MCGR: Avoid one level of primitive creation, use FillGradientPrimitive2D + // tooling to directly create needed geometry & color for getting better + // performance (partially compensate for potentially more expensive multi + // color gradients). + // To handle a primitive that needs paint, either use decompose, or - when you + // do not want that for any reason, e.g. extra primitives created - implement + // a direct handling in your primitive renderer. This is always possible + // since primitives by definition are self-contained what means they have all + // needed data locally available to do so. + // The question is the complexity to invest - the implemented decompose + // is always a good hint of what is needed to do this. In this case I decided + // to add some tooling methods to the primitive itself to support this. These + // are used in decompose and can be used - as here now - for direct handling, + // too. This is always a possibility in primitive handling - you can, but do not + // have to. + mpOutputDevice->SetFillColor( + Color(maBColorModifierStack.getModifiedColor(rPrimitive.getOuterColor()))); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawTransparent( + maCurrentTransformation, + basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect(rPrimitive.getOutputRange())), + 0.0); + + // paint solid fill steps by providing callback as lambda + auto aCallback([&rPrimitive, this](const basegfx::B2DHomMatrix& rMatrix, + const basegfx::BColor& rColor) { + // create part polygon + basegfx::B2DPolygon aNewPoly(rPrimitive.getUnitPolygon()); + aNewPoly.transform(rMatrix); + + // create solid fill + mpOutputDevice->SetFillColor(Color(maBColorModifierStack.getModifiedColor(rColor))); + mpOutputDevice->DrawTransparent(maCurrentTransformation, + basegfx::B2DPolyPolygon(aNewPoly), 0.0); + }); + + // call value generator to trigger callbacks + rPrimitive.generateMatricesAndColors(aCallback); + } + else + { + // use the decompose + process(rPrimitive); + } - // back to old OutDev - mpOutputDevice = pLastOutputDevice; - mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); + return; } - else - SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); + + // try to use vcl - since vcl uses the old gradient paint mechanisms this may + // create wrong geometries. If so, add another case above for useDecompose + Gradient aGradient(rFillGradient.getStyle(), + Color(rFillGradient.getColorStops().front().getStopColor()), + Color(rFillGradient.getColorStops().back().getStopColor())); + + aGradient.SetAngle(Degree10(static_cast<int>(basegfx::rad2deg<10>(rFillGradient.getAngle())))); + aGradient.SetBorder(rFillGradient.getBorder() * 100); + aGradient.SetOfsX(rFillGradient.getOffsetX() * 100.0); + aGradient.SetOfsY(rFillGradient.getOffsetY() * 100.0); + aGradient.SetSteps(rFillGradient.getSteps()); + + basegfx::B2DRange aOutputRange(rPrimitive.getOutputRange()); + aOutputRange.transform(maCurrentTransformation); + basegfx::B2DRange aFullRange(rPrimitive.getDefinitionRange()); + aFullRange.transform(maCurrentTransformation); + + const tools::Rectangle aOutputRectangle( + std::floor(aOutputRange.getMinX()), std::floor(aOutputRange.getMinY()), + std::ceil(aOutputRange.getMaxX()), std::ceil(aOutputRange.getMaxY())); + const tools::Rectangle aFullRectangle( + std::floor(aFullRange.getMinX()), std::floor(aFullRange.getMinY()), + std::ceil(aFullRange.getMaxX()), std::ceil(aFullRange.getMaxY())); + + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); + mpOutputDevice->IntersectClipRegion(aOutputRectangle); + mpOutputDevice->DrawGradient(aFullRectangle, aGradient); } -void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate) +void VclPixelProcessor2D::processPatternFillPrimitive2D( + const primitive2d::PatternFillPrimitive2D& rPrimitive) { - if (rCandidate.getShadowBlur() == 0) - { - process(rCandidate); + const basegfx::B2DRange& rReferenceRange = rPrimitive.getReferenceRange(); + if (rReferenceRange.isEmpty() || rReferenceRange.getWidth() <= 0.0 + || rReferenceRange.getHeight() <= 0.0) return; - } - basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - aRange.transform(maCurrentTransformation); - basegfx::B2DVector aBlurRadiusVector(rCandidate.getShadowBlur(), 0); - aBlurRadiusVector *= maCurrentTransformation; - const double fBlurRadius = aBlurRadiusVector.getLength(); + basegfx::B2DPolyPolygon aMask = rPrimitive.getMask(); + aMask.transform(maCurrentTransformation); + const basegfx::B2DRange aMaskRange(aMask.getB2DRange()); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); - if (aBufferDevice.isVisible()) - { - OutputDevice* pLastOutputDevice = mpOutputDevice; - mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); - - process(rCandidate); + if (aMaskRange.isEmpty() || aMaskRange.getWidth() <= 0.0 || aMaskRange.getHeight() <= 0.0) + return; - const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), - static_cast<long>(std::floor(aRange.getMinY())), - static_cast<long>(std::ceil(aRange.getMaxX())), - static_cast<long>(std::ceil(aRange.getMaxY()))); + sal_uInt32 nTileWidth, nTileHeight; + rPrimitive.getTileSize(nTileWidth, nTileHeight, getViewInformation2D()); + if (nTileWidth == 0 || nTileHeight == 0) + return; + Bitmap aTileImage = rPrimitive.createTileImage(nTileWidth, nTileHeight); + tools::Rectangle aMaskRect = vcl::unotools::rectangleFromB2DRectangle(aMaskRange); - BitmapEx bitmapEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); + // Unless smooth edges are needed, simply use clipping. + if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing()) + { + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); + mpOutputDevice->IntersectClipRegion(vcl::Region(aMask)); + Wallpaper aWallpaper(aTileImage); + aWallpaper.SetColor(COL_TRANSPARENT); + Point aPaperPt(aMaskRect.getX() % nTileWidth, aMaskRect.getY() % nTileHeight); + tools::Rectangle aPaperRect(aPaperPt, aTileImage.GetSizePixel()); + aWallpaper.SetRect(aPaperRect); + mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper); + return; + } - AlphaMask mask = ProcessAndBlurAlphaMask(bitmapEx.GetAlpha(), 0, fBlurRadius, 0); + impBufferDevice aBufferDevice(*mpOutputDevice, aMaskRange); - const basegfx::BColor aShadowColor( - maBColorModifierStack.getModifiedColor(rCandidate.getShadowColor())); + if (!aBufferDevice.isVisible()) + return; - Bitmap bitmap = bitmapEx.GetBitmap(); - bitmap.Erase(Color(aShadowColor)); - BitmapEx result(bitmap, mask); + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice = pLastOutputDevice; - mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); + // if the tile is a single pixel big, just flood fill with that pixel color + if (nTileWidth == 1 && nTileHeight == 1) + { + Color col = aTileImage.GetPixelColor(0, 0); + mpOutputDevice->SetLineColor(col); + mpOutputDevice->SetFillColor(col); + mpOutputDevice->DrawRect(aMaskRect); } else - SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); + { + Wallpaper aWallpaper(aTileImage); + aWallpaper.SetColor(COL_TRANSPARENT); + Point aPaperPt(aMaskRect.getX() % nTileWidth, aMaskRect.getY() % nTileHeight); + tools::Rectangle aPaperRect(aPaperPt, aTileImage.GetSizePixel()); + aWallpaper.SetRect(aPaperRect); + mpOutputDevice->DrawWallpaper(aMaskRect, aWallpaper); + } + + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + + // draw mask + VirtualDevice& rMask = aBufferDevice.getTransparence(); + rMask.SetLineColor(); + rMask.SetFillColor(COL_BLACK); + rMask.DrawPolyPolygon(aMask); + + // dump buffer to outdev + aBufferDevice.paint(); } } // end of namespace diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx index c3bd19141669..2c582c3347bc 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx @@ -29,19 +29,15 @@ namespace drawinglayer::primitive2d class PolyPolygonColorPrimitive2D; class PolygonHairlinePrimitive2D; class PolygonStrokePrimitive2D; -class WrongSpellPrimitive2D; -class TextSimplePortionPrimitive; class BitmapPrimitive2D; class PolyPolygonGradientPrimitive2D; class UnifiedTransparencePrimitive2D; class ControlPrimitive2D; -class PolygonStrokePrimitive2D; class FillHatchPrimitive2D; class BackgroundColorPrimitive2D; class BorderLinePrimitive2D; -class GlowPrimitive2D; -class ShadowPrimitive2D; -class SoftEdgePrimitive2D; +class FillGradientPrimitive2D; +class PatternFillPrimitive2D; } namespace drawinglayer::processor2d @@ -54,8 +50,10 @@ namespace drawinglayer::processor2d */ class VclPixelProcessor2D final : public VclProcessor2D { - struct Impl; - std::unique_ptr<Impl> m_pImpl; + AntialiasingFlags m_nOrigAntiAliasing; + + bool m_bRenderSimpleTextDirect; + bool m_bRenderDecoratedTextDirect; /* the local processor for BasePrimitive2D-Implementation based primitives, called from the common process()-implementation @@ -71,8 +69,6 @@ class VclPixelProcessor2D final : public VclProcessor2D bool tryDrawPolygonStrokePrimitive2DDirect(const primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency); - void - processWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive); void processTextSimplePortionPrimitive2D( const primitive2d::TextSimplePortionPrimitive2D& rCandidate); void processTextDecoratedPortionPrimitive2D( @@ -96,15 +92,12 @@ class VclPixelProcessor2D final : public VclProcessor2D processBorderLinePrimitive2D(const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder); void processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate); void processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate); - void processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate); - void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate); - void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate); + void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D& rPrimitive); + void processPatternFillPrimitive2D(const primitive2d::PatternFillPrimitive2D& rPrimitive); public: /// constructor/destructor - VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack - = basegfx::BColorModifierStack()); + VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev); virtual ~VclPixelProcessor2D() override; }; } // end of namespace drawinglayer::processor2d diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx b/drawinglayer/source/processor2d/vclprocessor2d.cxx index 5a0a85f911ef..b3f319f51d8f 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx @@ -23,8 +23,14 @@ #include "vclhelperbufferdevice.hxx" #include <cmath> #include <comphelper/string.hxx> +#include <comphelper/lok.hxx> +#include <svtools/optionsdrawinglayer.hxx> #include <tools/debug.hxx> +#include <tools/fract.hxx> +#include <utility> +#include <vcl/glyphitemcache.hxx> #include <vcl/graph.hxx> +#include <vcl/kernarray.hxx> #include <vcl/outdev.hxx> #include <rtl/ustrbuf.hxx> #include <sal/log.hxx> @@ -34,19 +40,14 @@ #include <basegfx/polygon/b2dpolygonclipper.hxx> #include <basegfx/color/bcolor.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> @@ -94,6 +95,29 @@ sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, } } +namespace +{ +/** helper to convert a MapMode to a transformation */ +basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode) +{ + basegfx::B2DHomMatrix aMapping; + const Fraction aNoScale(1, 1); + const Point& rOrigin(rMapMode.GetOrigin()); + + if (0 != rOrigin.X() || 0 != rOrigin.Y()) + { + aMapping.translate(rOrigin.X(), rOrigin.Y()); + } + + if (rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale) + { + aMapping.scale(double(rMapMode.GetScaleX()), double(rMapMode.GetScaleY())); + } + + return aMapping; +} +} + namespace drawinglayer::processor2d { // rendering support @@ -111,45 +135,64 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( basegfx::B2DVector aFontScaling, aTranslate; double fRotate, fShearX; aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX); + bool bPrimitiveAccepted(false); // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored, // especially if the effect is less than a pixel. if (std::abs(aFontScaling.getY() * fShearX) < 1) { - if (basegfx::fTools::less(aFontScaling.getX(), 0.0) - && basegfx::fTools::less(aFontScaling.getY(), 0.0)) + if (aFontScaling.getX() < 0.0 && aFontScaling.getY() < 0.0) { // handle special case: If scale is negative in (x,y) (3rd quadrant), it can // be expressed as rotation by PI. Use this since the Font rendering will not // apply the negative scales in any form aFontScaling = basegfx::absolute(aFontScaling); - fRotate += F_PI; + fRotate += M_PI; } - if (basegfx::fTools::more(aFontScaling.getX(), 0.0) - && basegfx::fTools::more(aFontScaling.getY(), 0.0)) + if (aFontScaling.getX() > 0.0 && aFontScaling.getY() > 0.0) { - // Get the VCL font (use FontHeight as FontWidth) - vcl::Font aFont(primitive2d::getVclFontFromFontAttribute( - rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(), - fRotate, rTextCandidate.getLocale())); + double fIgnoreRotate, fIgnoreShearX; - // set FillColor Attribute - const Color aFillColor(rTextCandidate.getTextFillColor()); - if (aFillColor != COL_TRANSPARENT) + basegfx::B2DVector aFontSize, aTextTranslate; + rTextCandidate.getTextTransform().decompose(aFontSize, aTextTranslate, fIgnoreRotate, + fIgnoreShearX); + + // tdf#153092 Ideally we don't have to scale the font and dxarray, but we might have + // to nevertheless if dealing with non integer sizes + const bool bScaleFont(aFontSize.getY() != std::round(aFontSize.getY()) + || comphelper::LibreOfficeKit::isActive()); + vcl::Font aFont; + + // Get the VCL font + if (!bScaleFont) { - aFont.SetFillColor(aFillColor); - aFont.SetTransparent(false); + aFont = primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), aFontSize.getX(), aFontSize.getY(), fRotate, + rTextCandidate.getLocale()); + } + else + { + aFont = primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), aFontScaling.getX(), aFontScaling.getY(), + fRotate, rTextCandidate.getLocale()); } // Don't draw fonts without height - if (aFont.GetFontHeight() <= 0) + Size aResultFontSize = aFont.GetFontSize(); + if (aResultFontSize.Height() <= 0) return; + // set FillColor Attribute + const Color aFillColor(rTextCandidate.getTextFillColor()); + aFont.SetTransparent(aFillColor.IsTransparent()); + aFont.SetFillColor(aFillColor); + // handle additional font attributes - const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP - = dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( + const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = nullptr; + if (rTextCandidate.getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) + pTCPP = static_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate); if (pTCPP != nullptr) @@ -251,83 +294,187 @@ void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D( aFont.SetShadow(true); } - // create transformed integer DXArray in view coordinate system - std::vector<long> aTransformedDXArray; + // create integer DXArray + KernArray aDXArray; if (!rTextCandidate.getDXArray().empty()) { - aTransformedDXArray.reserve(rTextCandidate.getDXArray().size()); - const basegfx::B2DVector aPixelVector(maCurrentTransformation - * basegfx::B2DVector(1.0, 0.0)); - const double fPixelVectorFactor(aPixelVector.getLength()); - - for (auto const& elem : rTextCandidate.getDXArray()) + double fPixelVectorFactor(1.0); + if (bScaleFont) { - aTransformedDXArray.push_back(basegfx::fround(elem * fPixelVectorFactor)); + const basegfx::B2DVector aPixelVector(maCurrentTransformation + * basegfx::B2DVector(1.0, 0.0)); + fPixelVectorFactor = aPixelVector.getLength(); } + + aDXArray.reserve(rTextCandidate.getDXArray().size()); + for (auto const& elem : rTextCandidate.getDXArray()) + aDXArray.push_back(elem * fPixelVectorFactor); } // set parameters and paint text snippet const basegfx::BColor aRGBFontColor( maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); - const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); - const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())); - const ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode()); + + // Store previous complex text layout state, to be restored after drawing + const vcl::text::ComplexTextLayoutFlags nOldLayoutMode(mpOutputDevice->GetLayoutMode()); if (rTextCandidate.getFontAttribute().getRTL()) { - ComplexTextLayoutFlags nRTLLayoutMode(nOldLayoutMode - & ~ComplexTextLayoutFlags::BiDiStrong); - nRTLLayoutMode - |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft; + vcl::text::ComplexTextLayoutFlags nRTLLayoutMode( + nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong); + nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl + | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; mpOutputDevice->SetLayoutMode(nRTLLayoutMode); } + else + { + // tdf#101686: This is LTR text, but the output device may have RTL state. + vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(nOldLayoutMode); + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl; + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong; + mpOutputDevice->SetLayoutMode(nLTRLayoutMode); + } - mpOutputDevice->SetFont(aFont); - mpOutputDevice->SetTextColor(Color(aRGBFontColor)); + Point aStartPoint; + bool bChangeMapMode(false); + if (!bScaleFont) + { + basegfx::B2DHomMatrix aCombinedTransform( + getTransformFromMapMode(mpOutputDevice->GetMapMode()) + * maCurrentTransformation); + + basegfx::B2DVector aCurrentScaling, aCurrentTranslate; + double fCurrentRotate; + aCombinedTransform.decompose(aCurrentScaling, aCurrentTranslate, fCurrentRotate, + fIgnoreShearX); + + const Point aOrigin( + basegfx::fround<tools::Long>(aCurrentTranslate.getX() / aCurrentScaling.getX()), + basegfx::fround<tools::Long>(aCurrentTranslate.getY() + / aCurrentScaling.getY())); + + Fraction aScaleX(aCurrentScaling.getX()); + if (!aScaleX.IsValid()) + { + SAL_WARN("drawinglayer", "invalid X Scale"); + return; + } + + Fraction aScaleY(aCurrentScaling.getY()); + if (!aScaleY.IsValid()) + { + SAL_WARN("drawinglayer", "invalid Y Scale"); + return; + } - OUString aText(rTextCandidate.getText()); - sal_Int32 nPos = rTextCandidate.getTextPosition(); - sal_Int32 nLen = rTextCandidate.getTextLength(); + MapMode aMapMode(mpOutputDevice->GetMapMode().GetMapUnit(), aOrigin, aScaleX, + aScaleY); - long* pDXArray = !aTransformedDXArray.empty() ? aTransformedDXArray.data() : nullptr; + if (fCurrentRotate) + aTextTranslate *= basegfx::utils::createRotateB2DHomMatrix(fCurrentRotate); + aStartPoint = Point(basegfx::fround<tools::Long>(aTextTranslate.getX()), + basegfx::fround<tools::Long>(aTextTranslate.getY())); - if (rTextCandidate.isFilled()) + bChangeMapMode = aMapMode != mpOutputDevice->GetMapMode(); + if (bChangeMapMode) + { + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + } + } + else { - basegfx::B2DVector aOldFontScaling, aOldTranslate; - double fOldRotate, fOldShearX; - rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate, - fOldRotate, fOldShearX); - - long nWidthToFill = static_cast<long>( - rTextCandidate.getWidthToFill() * aFontScaling.getX() / aOldFontScaling.getX()); - - long nWidth - = mpOutputDevice->GetTextArray(rTextCandidate.getText(), pDXArray, 0, 1); - long nChars = 2; - if (nWidth) - nChars = nWidthToFill / nWidth; - - OUStringBuffer aFilled; - comphelper::string::padToLength(aFilled, nChars, aText[0]); - aText = aFilled.makeStringAndClear(); - nPos = 0; - nLen = nChars; + const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); + double aPointX = aPoint.getX(), aPointY = aPoint.getY(); + + if (!comphelper::LibreOfficeKit::isActive()) + { + // aFont has an integer size; we must scale a bit for precision + double nFontScalingFixY = aFontScaling.getY() / aResultFontSize.Height(); + double nFontScalingFixX + = aFontScaling.getX() + / (aResultFontSize.Width() ? aResultFontSize.Width() + : aResultFontSize.Height()); + +#ifdef _WIN32 + if (aResultFontSize.Width() + && aResultFontSize.Width() != aResultFontSize.Height()) + { + // See getVclFontFromFontAttribute in drawinglayer/source/primitive2d/textlayoutdevice.cxx + vcl::Font aUnscaledTest(aFont); + aUnscaledTest.SetFontSize({ 0, aResultFontSize.Height() }); + const FontMetric aUnscaledFontMetric( + Application::GetDefaultDevice()->GetFontMetric(aUnscaledTest)); + if (aUnscaledFontMetric.GetAverageFontWidth() > 0) + { + double nExistingXScale = static_cast<double>(aResultFontSize.Width()) + / aUnscaledFontMetric.GetAverageFontWidth(); + nFontScalingFixX + = aFontScaling.getX() / aFontScaling.getY() / nExistingXScale; + } + } +#endif + + if (!rtl_math_approxEqual(nFontScalingFixY, 1.0) + || !rtl_math_approxEqual(nFontScalingFixX, 1.0)) + { + MapMode aMapMode = mpOutputDevice->GetMapMode(); + aMapMode.SetScaleX(aMapMode.GetScaleX() * nFontScalingFixX); + aMapMode.SetScaleY(aMapMode.GetScaleY() * nFontScalingFixY); + + const bool bValidScaling + = aMapMode.GetScaleX().IsValid() && aMapMode.GetScaleY().IsValid(); + if (!bValidScaling) + SAL_WARN("drawinglayer", "skipping invalid scaling"); + else + { + assert(nFontScalingFixX != 0 && nFontScalingFixY != 0 + && "or bValidScaling would be false"); + + Point origin = aMapMode.GetOrigin(); + + mpOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpOutputDevice->SetRelativeMapMode(aMapMode); + bChangeMapMode = true; + + aPointX = (aPointX + origin.X()) / nFontScalingFixX - origin.X(); + aPointY = (aPointY + origin.Y()) / nFontScalingFixY - origin.Y(); + } + } + } + + aStartPoint = Point(basegfx::fround<tools::Long>(aPointX), + basegfx::fround<tools::Long>(aPointY)); } - if (!aTransformedDXArray.empty()) + // tdf#152990 set the font after the MapMode is (potentially) set so canvas uses the desired + // font size + mpOutputDevice->SetFont(aFont); + mpOutputDevice->SetTextColor(Color(aRGBFontColor)); + + if (!aDXArray.empty()) { - mpOutputDevice->DrawTextArray(aStartPoint, aText, pDXArray, nPos, nLen); + const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs( + mpOutputDevice, rTextCandidate.getText(), rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); + mpOutputDevice->DrawTextArray( + aStartPoint, rTextCandidate.getText(), aDXArray, + rTextCandidate.getKashidaArray(), rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength(), SalLayoutFlags::NONE, pGlyphs); } else { - mpOutputDevice->DrawText(aStartPoint, aText, nPos, nLen); + mpOutputDevice->DrawText(aStartPoint, rTextCandidate.getText(), + rTextCandidate.getTextPosition(), + rTextCandidate.getTextLength()); } - if (rTextCandidate.getFontAttribute().getRTL()) - { - mpOutputDevice->SetLayoutMode(nOldLayoutMode); - } + // Restore previous layout mode + mpOutputDevice->SetLayoutMode(nOldLayoutMode); + + if (bChangeMapMode) + mpOutputDevice->Pop(); bPrimitiveAccepted = true; } @@ -352,8 +499,7 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); aLocalPolygon.transform(maCurrentTransformation); - if (bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() - && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()) + if (bPixelBased && getViewInformation2D().getPixelSnapHairline()) { // #i98289# // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete @@ -366,18 +512,18 @@ void VclProcessor2D::RenderPolygonHairlinePrimitive2D( mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0); } -// direct draw of transformed BitmapEx primitive +// direct draw of transformed Bitmap primitive void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) { - BitmapEx aBitmapEx(VCLUnoHelper::GetBitmap(rBitmapCandidate.getXBitmap())); + Bitmap aBitmap(rBitmapCandidate.getBitmap()); const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); if (maBColorModifierStack.count()) { - aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack); + aBitmap = aBitmap.Modify(maBColorModifierStack); - if (aBitmapEx.IsEmpty()) + if (aBitmap.IsEmpty()) { // color gets completely replaced, get it const basegfx::BColor aModifiedColor( @@ -397,249 +543,248 @@ void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx). // draw using OutputDevice'sDrawTransformedBitmapEx - mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx); + mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmap); } void VclProcessor2D::RenderFillGraphicPrimitive2D( const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) { + if (rFillBitmapCandidate.getTransparency() < 0.0 + || rFillBitmapCandidate.getTransparency() > 1.0) + { + // invalid transparence, done + return; + } + + bool bPrimitiveAccepted = RenderFillGraphicPrimitive2DImpl(rFillBitmapCandidate); + + if (!bPrimitiveAccepted) + { + // do not accept, use decomposition + process(rFillBitmapCandidate); + } +} + +bool VclProcessor2D::RenderFillGraphicPrimitive2DImpl( + const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) +{ const attribute::FillGraphicAttribute& rFillGraphicAttribute( rFillBitmapCandidate.getFillGraphic()); - bool bPrimitiveAccepted(false); // #121194# when tiling is used and content is bitmap-based, do direct tiling in the // renderer on pixel base to ensure tight fitting. Do not do this when // the fill is rotated or sheared. - if (rFillGraphicAttribute.getTiling()) - { - // content is bitmap(ex) - // - // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use - // the primitive representation of the vector data directly. - // - // when graphic is animated, force decomposition to use the correct graphic, else - // fill style will not be animated - if (GraphicType::Bitmap == rFillGraphicAttribute.getGraphic().GetType() - && !rFillGraphicAttribute.getGraphic().getVectorGraphicData() - && !rFillGraphicAttribute.getGraphic().IsAnimated()) - { - // decompose matrix to check for shear, rotate and mirroring - basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation - * rFillBitmapCandidate.getTransformation()); - basegfx::B2DVector aScale, aTranslate; - double fRotate, fShearX; - aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); - - // when nopt rotated/sheared - if (basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX)) - { - // no shear or rotate, draw direct in pixel coordinates - bPrimitiveAccepted = true; + if (!rFillGraphicAttribute.getTiling()) + return false; + + // content is bitmap(ex) + // + // for Vector Graphic Data (SVG, EMF+) support, force decomposition when present. This will lead to use + // the primitive representation of the vector data directly. + // + // when graphic is animated, force decomposition to use the correct graphic, else + // fill style will not be animated + if (GraphicType::Bitmap != rFillGraphicAttribute.getGraphic().GetType() + || rFillGraphicAttribute.getGraphic().getVectorGraphicData() + || rFillGraphicAttribute.getGraphic().IsAnimated()) + return false; + + // decompose matrix to check for shear, rotate and mirroring + basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation + * rFillBitmapCandidate.getTransformation()); + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); - // transform object range to device coordinates (pixels). Use - // the device transformation for better accuracy - basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale); - aObjectRange.transform(mpOutputDevice->GetViewTransformation()); + // when nopt rotated/sheared + if (!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX)) + return false; - // extract discrete size of object - const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth())); - const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight())); + // no shear or rotate, draw direct in pixel coordinates - // only do something when object has a size in discrete units - if (nOWidth > 0 && nOHeight > 0) - { - // transform graphic range to device coordinates (pixels). Use - // the device transformation for better accuracy - basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); - aGraphicRange.transform(mpOutputDevice->GetViewTransformation() - * aLocalTransform); - - // extract discrete size of graphic - // caution: when getting to zero, nothing would be painted; thus, do not allow this - const sal_Int32 nBWidth( - std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth()))); - const sal_Int32 nBHeight( - std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight()))); - - // only do something when bitmap fill has a size in discrete units - if (nBWidth > 0 && nBHeight > 0) - { - // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it - // in vcl many times, create a size-optimized version - const Size aNeededBitmapSizePixel(nBWidth, nBHeight); - BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); - const bool bPreScaled(nBWidth * nBHeight < (250 * 250)); - - // ... but only up to a maximum size, else it gets too expensive - if (bPreScaled) - { - // if color depth is below 24bit, expand before scaling for better quality. - // This is even needed for low colors, else the scale will produce - // a bitmap in gray or Black/White (!) - if (aBitmapEx.GetBitCount() < 24) - { - aBitmapEx.Convert(BmpConversion::N24Bit); - } + // transform object range to device coordinates (pixels). Use + // the device transformation for better accuracy + basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale); + aObjectRange.transform(mpOutputDevice->GetViewTransformation()); - aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate); - } + // extract discrete size of object + const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth())); + const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight())); - bool bPainted(false); + // only do something when object has a size in discrete units + if (nOWidth <= 0 || nOHeight <= 0) + return true; - if (maBColorModifierStack.count()) - { - // when color modifier, apply to bitmap - aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack); + // transform graphic range to device coordinates (pixels). Use + // the device transformation for better accuracy + basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); + aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform); - // impModifyBitmapEx uses empty bitmap as sign to return that - // the content will be completely replaced to mono color, use shortcut - if (aBitmapEx.IsEmpty()) - { - // color gets completely replaced, get it - const basegfx::BColor aModifiedColor( - maBColorModifierStack.getModifiedColor(basegfx::BColor())); - basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); - aPolygon.transform(aLocalTransform); + // extract discrete size of graphic + // caution: when getting to zero, nothing would be painted; thus, do not allow this + const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth()))); + const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight()))); - mpOutputDevice->SetFillColor(Color(aModifiedColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawPolygon(aPolygon); + // nBWidth, nBHeight is the pixel size of the needed bitmap. To not need to scale it + // in vcl many times, create a size-optimized version + const Size aNeededBitmapSizePixel(nBWidth, nBHeight); + Bitmap aBitmap(rFillGraphicAttribute.getGraphic().GetBitmap()); + const bool bPreScaled(nBWidth * nBHeight < (250 * 250)); - bPainted = true; - } - } + // ... but only up to a maximum size, else it gets too expensive + if (bPreScaled) + { + // if color depth is below 24bit, expand before scaling for better quality. + // This is even needed for low colors, else the scale will produce + // a bitmap in gray or Black/White (!) + if (isPalettePixelFormat(aBitmap.getPixelFormat())) + { + aBitmap.Convert(BmpConversion::N24Bit); + } - if (!bPainted) - { - sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX())); - sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY())); - const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX())); - const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY())); - sal_Int32 nPosX(0); - sal_Int32 nPosY(0); - - if (nBLeft > nOLeft) - { - const sal_Int32 nDiff((nBLeft / nBWidth) + 1); + aBitmap.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate); + } - nPosX -= nDiff; - nBLeft -= nDiff * nBWidth; - } + if (rFillBitmapCandidate.hasTransparency()) + aBitmap.BlendAlpha( + static_cast<sal_uInt8>(255 - (rFillBitmapCandidate.getTransparency() * 255))); - if (nBLeft + nBWidth <= nOLeft) - { - const sal_Int32 nDiff(-nBLeft / nBWidth); + if (maBColorModifierStack.count()) + { + // when color modifier, apply to bitmap + aBitmap = aBitmap.Modify(maBColorModifierStack); - nPosX += nDiff; - nBLeft += nDiff * nBWidth; - } + // ModifyBitmapEx uses empty bitmap as sign to return that + // the content will be completely replaced to mono color, use shortcut + if (aBitmap.IsEmpty()) + { + // color gets completely replaced, get it + const basegfx::BColor aModifiedColor( + maBColorModifierStack.getModifiedColor(basegfx::BColor())); + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + aPolygon.transform(aLocalTransform); - if (nBTop > nOTop) - { - const sal_Int32 nDiff((nBTop / nBHeight) + 1); + mpOutputDevice->SetFillColor(Color(aModifiedColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aPolygon); - nPosY -= nDiff; - nBTop -= nDiff * nBHeight; - } + return true; + } + } - if (nBTop + nBHeight <= nOTop) - { - const sal_Int32 nDiff(-nBTop / nBHeight); + sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX())); + sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY())); + const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX())); + const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY())); + sal_Int32 nPosX(0); + sal_Int32 nPosY(0); - nPosY += nDiff; - nBTop += nDiff * nBHeight; - } + if (nBLeft > nOLeft) + { + const sal_Int32 nDiff((nBLeft / nBWidth) + 1); - // prepare OutDev - const Point aEmptyPoint(0, 0); - const ::tools::Rectangle aVisiblePixel( - aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); - const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); - mpOutputDevice->EnableMapMode(false); + nPosX -= nDiff; + nBLeft -= nDiff * nBWidth; + } - // check if offset is used - const sal_Int32 nOffsetX( - basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); + if (nBLeft + nBWidth <= nOLeft) + { + const sal_Int32 nDiff(-nBLeft / nBWidth); - if (nOffsetX) - { - // offset in X, so iterate over Y first and draw lines - for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; - nYPos += nBHeight, nPosY++) - { - for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX - : nBLeft); - nXPos < nOLeft + nOWidth; nXPos += nBWidth) - { - const ::tools::Rectangle aOutRectPixel( - Point(nXPos, nYPos), aNeededBitmapSizePixel); - - if (aOutRectPixel.IsOver(aVisiblePixel)) - { - if (bPreScaled) - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aBitmapEx); - } - else - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, - aBitmapEx); - } - } - } - } - } - else - { - // check if offset is used - const sal_Int32 nOffsetY( - basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); + nPosX += nDiff; + nBLeft += nDiff * nBWidth; + } - // possible offset in Y, so iterate over X first and draw columns - for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; - nXPos += nBWidth, nPosX++) - { - for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY - : nBTop); - nYPos < nOTop + nOHeight; nYPos += nBHeight) - { - const ::tools::Rectangle aOutRectPixel( - Point(nXPos, nYPos), aNeededBitmapSizePixel); - - if (aOutRectPixel.IsOver(aVisiblePixel)) - { - if (bPreScaled) - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aBitmapEx); - } - else - { - mpOutputDevice->DrawBitmapEx( - aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, - aBitmapEx); - } - } - } - } - } + if (nBTop > nOTop) + { + const sal_Int32 nDiff((nBTop / nBHeight) + 1); - // restore OutDev - mpOutputDevice->EnableMapMode(bWasEnabled); - } + nPosY -= nDiff; + nBTop -= nDiff * nBHeight; + } + + if (nBTop + nBHeight <= nOTop) + { + const sal_Int32 nDiff(-nBTop / nBHeight); + + nPosY += nDiff; + nBTop += nDiff * nBHeight; + } + + // prepare OutDev + const Point aEmptyPoint(0, 0); + // the visible rect, in pixels + const ::tools::Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); + const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); + mpOutputDevice->EnableMapMode(false); + + // check if offset is used + const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); + const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); + + // if the tile is a single pixel big, just flood fill with that pixel color + if (nOffsetX == 0 && nOffsetY == 0 && aNeededBitmapSizePixel.getWidth() == 1 + && aNeededBitmapSizePixel.getHeight() == 1) + { + Color col = aBitmap.GetPixelColor(0, 0); + mpOutputDevice->SetLineColor(col); + mpOutputDevice->SetFillColor(col); + mpOutputDevice->DrawRect(aVisiblePixel); + } + else if (nOffsetX) + { + // offset in X, so iterate over Y first and draw lines + for (sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++) + { + for (sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft); + nXPos < nOLeft + nOWidth; nXPos += nBWidth) + { + const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); + + if (aOutRectPixel.Overlaps(aVisiblePixel)) + { + if (bPreScaled) + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmap); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmap); } } } } } - - if (!bPrimitiveAccepted) + else // nOffsetY is used { - // do not accept, use decomposition - process(rFillBitmapCandidate); + // possible offset in Y, so iterate over X first and draw columns + for (sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++) + { + for (sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop); + nYPos < nOTop + nOHeight; nYPos += nBHeight) + { + const ::tools::Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); + + if (aOutRectPixel.Overlaps(aVisiblePixel)) + { + if (bPreScaled) + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmap); + } + else + { + mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), + aNeededBitmapSizePixel, aBitmap); + } + } + } + } } + + // restore OutDev + mpOutputDevice->EnableMapMode(bWasEnabled); + return true; } // direct draw of Graphic @@ -672,7 +817,8 @@ void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( case GraphicType::Bitmap: { if (!rFillGraphicAttribute.getGraphic().IsTransparent() - && !rFillGraphicAttribute.getGraphic().IsAlpha()) + && !rFillGraphicAttribute.getGraphic().IsAlpha() + && !rPolygonCandidate.hasTransparency()) { // bitmap is not transparent and has no alpha const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count()); @@ -757,7 +903,7 @@ void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D( } } -// mask group. Force output to VDev and create mask from given mask +// mask group void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate) { if (rMaskCandidate.getChildren().empty()) @@ -769,6 +915,16 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive return; aMask.transform(maCurrentTransformation); + + // Unless smooth edges are needed, simply use clipping. + if (basegfx::utils::isRectangle(aMask) || !getViewInformation2D().getUseAntiAliasing()) + { + auto popIt = mpOutputDevice->ScopedPush(vcl::PushFlags::CLIPREGION); + mpOutputDevice->IntersectClipRegion(vcl::Region(aMask)); + process(rMaskCandidate.getChildren()); + return; + } + const basegfx::B2DRange aRange(basegfx::utils::getRange(aMask)); impBufferDevice aBufferDevice(*mpOutputDevice, aRange); @@ -785,28 +941,11 @@ void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive // back to old OutDev mpOutputDevice = pLastOutputDevice; - // if the mask fills the whole area we can skip - // creating a transparent vd and filling it. - if (!basegfx::utils::isRectangle(aMask)) - { - // draw mask - if (getOptionsDrawinglayer().IsAntiAliasing()) - { - // with AA, use 8bit AlphaMask to get nice borders - VirtualDevice& rTransparence = aBufferDevice.getTransparence(); - rTransparence.SetLineColor(); - rTransparence.SetFillColor(COL_BLACK); - rTransparence.DrawPolyPolygon(aMask); - } - else - { - // No AA, use 1bit mask - VirtualDevice& rMask = aBufferDevice.getMask(); - rMask.SetLineColor(); - rMask.SetFillColor(COL_BLACK); - rMask.DrawPolyPolygon(aMask); - } - } + // draw mask + VirtualDevice& rMask = aBufferDevice.getTransparence(); + rMask.SetLineColor(); + rMask.SetFillColor(COL_BLACK); + rMask.DrawPolyPolygon(aMask); // dump buffer to outdev aBufferDevice.paint(); @@ -841,14 +980,13 @@ void VclProcessor2D::RenderUnifiedTransparencePrimitive2D( // transparence is in visible range basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aRange.transform(maCurrentTransformation); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); if (aBufferDevice.isVisible()) { // remember last OutDev and set to content OutputDevice* pLastOutputDevice = mpOutputDevice; mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); // paint content to it process(rTransCandidate.getChildren()); @@ -871,7 +1009,7 @@ void VclProcessor2D::RenderTransparencePrimitive2D( basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D())); aRange.transform(maCurrentTransformation); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); if (!aBufferDevice.isVisible()) return; @@ -879,7 +1017,6 @@ void VclProcessor2D::RenderTransparencePrimitive2D( // remember last OutDev and set to content OutputDevice* pLastOutputDevice = mpOutputDevice; mpOutputDevice = &aBufferDevice.getContent(); - mpOutputDevice->Erase(); // paint content to it process(rTransCandidate.getChildren()); @@ -891,13 +1028,11 @@ void VclProcessor2D::RenderTransparencePrimitive2D( basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack); maBColorModifierStack = basegfx::BColorModifierStack(); - mpOutputDevice->Erase(); - // paint mask to it (always with transparence intensities, evtl. with AA) process(rTransCandidate.getTransparence()); // back to old color stack - maBColorModifierStack = aLastBColorModifierStack; + maBColorModifierStack = std::move(aLastBColorModifierStack); // back to old OutDev mpOutputDevice = pLastOutputDevice; @@ -917,19 +1052,17 @@ void VclProcessor2D::RenderTransformPrimitive2D( // create new transformations for CurrentTransformation // and for local ViewInformation2D maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation(); - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), - getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(), - getViewInformation2D().getVisualizedPage(), getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation() + * rTransformCandidate.getTransformation()); + setViewInformation2D(aViewInformation2D); // process content process(rTransformCandidate.getChildren()); // restore transformations maCurrentTransformation = aLastCurrentTransformation; - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); } // new XDrawPage for ViewInformation2D @@ -940,18 +1073,15 @@ void VclProcessor2D::RenderPagePreviewPrimitive2D( const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); // create new local ViewInformation2D - const geometry::ViewInformation2D aViewInformation2D( - getViewInformation2D().getObjectTransformation(), - getViewInformation2D().getViewTransformation(), getViewInformation2D().getViewport(), - rPagePreviewCandidate.getXDrawPage(), getViewInformation2D().getViewTime(), - getViewInformation2D().getExtendedInformationSequence()); - updateViewInformation(aViewInformation2D); + geometry::ViewInformation2D aViewInformation2D(getViewInformation2D()); + aViewInformation2D.setVisualizedPage(rPagePreviewCandidate.getXDrawPage()); + setViewInformation2D(aViewInformation2D); // process decomposed content process(rPagePreviewCandidate); // restore transformations - updateViewInformation(aLastViewInformation2D); + setViewInformation2D(aLastViewInformation2D); } // marker @@ -962,11 +1092,11 @@ void VclProcessor2D::RenderMarkerArrayPrimitive2D( const std::vector<basegfx::B2DPoint>& rPositions = rMarkArrayCandidate.getPositions(); const sal_uInt32 nCount(rPositions.size()); - if (!(nCount && !rMarkArrayCandidate.getMarker().IsEmpty())) + if (!nCount || rMarkArrayCandidate.getMarker().IsEmpty()) return; // get pixel size - const BitmapEx& rMarker(rMarkArrayCandidate.getMarker()); + const Bitmap& rMarker(rMarkArrayCandidate.getMarker()); const Size aBitmapSize(rMarker.GetSizePixel()); if (!(aBitmapSize.Width() && aBitmapSize.Height())) @@ -990,8 +1120,8 @@ void VclProcessor2D::RenderMarkerArrayPrimitive2D( { const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * pos) - aDiscreteHalfSize); - const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), - basegfx::fround(aDiscreteTopLeft.getY())); + const Point aDiscretePoint(basegfx::fround<tools::Long>(aDiscreteTopLeft.getX()), + basegfx::fround<tools::Long>(aDiscreteTopLeft.getY())); mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker); } @@ -1011,8 +1141,8 @@ void VclProcessor2D::RenderPointArrayPrimitive2D( for (auto const& pos : rPositions) { const basegfx::B2DPoint aViewPosition(maCurrentTransformation * pos); - const Point aPos(basegfx::fround(aViewPosition.getX()), - basegfx::fround(aViewPosition.getY())); + const Point aPos(basegfx::fround<tools::Long>(aViewPosition.getX()), + basegfx::fround<tools::Long>(aViewPosition.getY())); mpOutputDevice->DrawPixel(aPos, aVCLColor); } @@ -1027,7 +1157,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D( const double fLineWidth(rLineAttribute.getWidth()); bool bDone(false); - if (basegfx::fTools::more(fLineWidth, 0.0)) + if (fLineWidth > 0.0) { const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0)); @@ -1058,7 +1188,7 @@ void VclProcessor2D::RenderPolygonStrokePrimitive2D( if (nCount) { - const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); + const bool bAntiAliased(getViewInformation2D().getUseAntiAliasing()); aHairlinePolyPolygon.transform(maCurrentTransformation); if (bAntiAliased) @@ -1271,26 +1401,12 @@ void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEp } } -void VclProcessor2D::RenderObjectInfoPrimitive2D( - const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D) -{ - // remember current ObjectInfoPrimitive2D and set new current one (build a stack - push) - const primitive2d::ObjectInfoPrimitive2D* pLast(getObjectInfoPrimitive2D()); - mpObjectInfoPrimitive2D = &rObjectInfoPrimitive2D; - - // process content - process(rObjectInfoPrimitive2D.getChildren()); - - // restore current ObjectInfoPrimitive2D (pop) - mpObjectInfoPrimitive2D = pLast; -} - void VclProcessor2D::RenderSvgLinearAtomPrimitive2D( const primitive2d::SvgLinearAtomPrimitive2D& rCandidate) { const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA()); - if (!basegfx::fTools::more(fDelta, 0.0)) + if (fDelta <= 0.0) return; const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); @@ -1300,7 +1416,7 @@ void VclProcessor2D::RenderSvgLinearAtomPrimitive2D( const basegfx::B2DVector aDiscreteVector( getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373)); + const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2)); // use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit)); @@ -1334,7 +1450,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D( { const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA()); - if (!basegfx::fTools::more(fDeltaScale, 0.0)) + if (fDeltaScale <= 0.0) return; const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); @@ -1344,7 +1460,7 @@ void VclProcessor2D::RenderSvgRadialAtomPrimitive2D( const basegfx::B2DVector aDiscreteVector( getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); - const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373)); + const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / M_SQRT2)); // use color distance and discrete lengths to calculate step count const sal_uInt32 nSteps( @@ -1482,25 +1598,40 @@ void VclProcessor2D::adaptTextToFillDrawMode() const mpOutputDevice->SetDrawMode(nAdaptedDrawMode); } -// process support +void VclProcessor2D::onViewInformation2DChanged() +{ + // apply AntiAlias information to target device + if (getViewInformation2D().getUseAntiAliasing()) + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() + | AntialiasingFlags::Enable); + else + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() + & ~AntialiasingFlags::Enable); + + // apply DrawModeFlags to target device + if (getViewInformation2D().getDrawModeFlags() != mpOutputDevice->GetDrawMode()) + mpOutputDevice->SetDrawMode(getViewInformation2D().getDrawModeFlags()); +} +// process support VclProcessor2D::VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, - OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack) + OutputDevice& rOutDev) : BaseProcessor2D(rViewInformation) , mpOutputDevice(&rOutDev) - , maBColorModifierStack(rInitStack) - , maCurrentTransformation() - , maDrawinglayerOpt() + , maBColorModifierStack() , mnPolygonStrokePrimitive2D(0) - , mpObjectInfoPrimitive2D(nullptr) + , mnOriginalAA(rOutDev.GetAntialiasing()) { // set digit language, derived from SvtCTLOptions to have the correct // number display for arabic/hindi numerals rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage()); + + // NOTE: to save/restore original AntiAliasing mode we need + // to use mnOriginalAA here - OutputDevice::Push/Pop does not + // offer that } -VclProcessor2D::~VclProcessor2D() {} +VclProcessor2D::~VclProcessor2D() { mpOutputDevice->SetAntialiasing(mnOriginalAA); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor2d/vclprocessor2d.hxx b/drawinglayer/source/processor2d/vclprocessor2d.hxx index dbca9ed4ba25..10beddf6bd10 100644 --- a/drawinglayer/source/processor2d/vclprocessor2d.hxx +++ b/drawinglayer/source/processor2d/vclprocessor2d.hxx @@ -22,7 +22,7 @@ #include <drawinglayer/processor2d/baseprocessor2d.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> #include <basegfx/color/bcolormodifier.hxx> -#include <svtools/optionsdrawinglayer.hxx> +#include <vcl/rendercontext/AntialiasingFlags.hxx> #include <vcl/vclptr.hxx> class OutputDevice; @@ -33,9 +33,7 @@ class TextSimplePortionPrimitive2D; class PolygonHairlinePrimitive2D; class BitmapPrimitive2D; class FillGraphicPrimitive2D; -class PolyPolygonGradientPrimitive2D; class PolyPolygonGraphicPrimitive2D; -class MetafilePrimitive2D; class MaskPrimitive2D; class UnifiedTransparencePrimitive2D; class TransparencePrimitive2D; @@ -44,10 +42,8 @@ class MarkerArrayPrimitive2D; class PointArrayPrimitive2D; class ModifiedColorPrimitive2D; class PolygonStrokePrimitive2D; -class ControlPrimitive2D; class PagePreviewPrimitive2D; class EpsPrimitive2D; -class ObjectInfoPrimitive2D; class SvgLinearAtomPrimitive2D; class SvgRadialAtomPrimitive2D; } @@ -73,15 +69,12 @@ protected: // ViewInformation2D cannot directly be used, but needs to be kept up to date basegfx::B2DHomMatrix maCurrentTransformation; - // SvtOptionsDrawinglayer incarnation to react on diverse settings - const SvtOptionsDrawinglayer maDrawinglayerOpt; - // stack value (increment and decrement) to count how deep we are in // PolygonStrokePrimitive2D's decompositions (normally only one) sal_uInt32 mnPolygonStrokePrimitive2D; - // currently used ObjectInfoPrimitive2D - const primitive2d::ObjectInfoPrimitive2D* mpObjectInfoPrimitive2D; + // AntialiasingFlags, saved at construction time + const AntialiasingFlags mnOriginalAA; // common VCL rendering support void RenderTextSimpleOrDecoratedPortionPrimitive2D( @@ -109,8 +102,6 @@ protected: void RenderPolygonStrokePrimitive2D( const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate); void RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D); - void - RenderObjectInfoPrimitive2D(const primitive2d::ObjectInfoPrimitive2D& rObjectInfoPrimitive2D); void RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate); void RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate); @@ -118,20 +109,17 @@ protected: void adaptLineToFillDrawMode() const; void adaptTextToFillDrawMode() const; + // allow to react on changes of ViewInformation2D + virtual void onViewInformation2DChanged() override; + public: // constructor/destructor - VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev, - const basegfx::BColorModifierStack& rInitStack = basegfx::BColorModifierStack()); + VclProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev); virtual ~VclProcessor2D() override; - // access to Drawinglayer configuration options - const SvtOptionsDrawinglayer& getOptionsDrawinglayer() const { return maDrawinglayerOpt; } - - // access to currently used ObjectInfoPrimitive2D - const primitive2d::ObjectInfoPrimitive2D* getObjectInfoPrimitive2D() const - { - return mpObjectInfoPrimitive2D; - } +private: + bool RenderFillGraphicPrimitive2DImpl( + const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate); }; } // end of namespace drawinglayer::processor2d diff --git a/drawinglayer/source/processor3d/baseprocessor3d.cxx b/drawinglayer/source/processor3d/baseprocessor3d.cxx index 508ee553f31c..7fd2a80a9988 100644 --- a/drawinglayer/source/processor3d/baseprocessor3d.cxx +++ b/drawinglayer/source/processor3d/baseprocessor3d.cxx @@ -18,7 +18,7 @@ */ #include <drawinglayer/processor3d/baseprocessor3d.hxx> -#include <comphelper/sequence.hxx> +#include <utility> using namespace com::sun::star; @@ -30,8 +30,8 @@ namespace drawinglayer::processor3d { } - BaseProcessor3D::BaseProcessor3D(const geometry::ViewInformation3D& rViewInformation) - : maViewInformation3D(rViewInformation) + BaseProcessor3D::BaseProcessor3D(geometry::ViewInformation3D aViewInformation) + : maViewInformation3D(std::move(aViewInformation)) { } @@ -49,27 +49,27 @@ namespace drawinglayer::processor3d for(size_t a(0); a < nCount; a++) { // get reference - const primitive3d::Primitive3DReference xReference(rSource[a]); + const primitive3d::Primitive3DReference& xReference(rSource[a]); if(xReference.is()) { - // try to cast to BasePrimitive3D implementation - const primitive3d::BasePrimitive3D* pBasePrimitive = dynamic_cast< const primitive3d::BasePrimitive3D* >(xReference.get()); - - if(pBasePrimitive) - { - processBasePrimitive3D(*pBasePrimitive); - } - else - { - // unknown implementation, use UNO API call instead and process recursively - const uno::Sequence< beans::PropertyValue >& rViewParameters(getViewInformation3D().getViewInformationSequence()); - process(comphelper::sequenceToContainer<primitive3d::Primitive3DContainer>(xReference->getDecomposition(rViewParameters))); - } + const primitive3d::BasePrimitive3D* pBasePrimitive = static_cast< const primitive3d::BasePrimitive3D* >(xReference.get()); + processBasePrimitive3D(*pBasePrimitive); } } } + void BaseProcessor3D::setViewInformation3D(const geometry::ViewInformation3D& rNew) + { + if (rNew != maViewInformation3D) + { + // set if changed + maViewInformation3D = rNew; + + // allow reaction on change + onViewInformation3DChanged(); + } + } } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/processor3d/cutfindprocessor3d.cxx b/drawinglayer/source/processor3d/cutfindprocessor3d.cxx index 432ef87d3a85..59228aeca0ed 100644 --- a/drawinglayer/source/processor3d/cutfindprocessor3d.cxx +++ b/drawinglayer/source/processor3d/cutfindprocessor3d.cxx @@ -37,8 +37,6 @@ namespace drawinglayer::processor3d : BaseProcessor3D(rViewInformation), maFront(rFront), maBack(rBack), - maResult(), - maCombinedTransform(), mbAnyHit(bAnyHit) { } @@ -76,7 +74,7 @@ namespace drawinglayer::processor3d aLastViewInformation3D.getDeviceToView(), aLastViewInformation3D.getViewTime(), aLastViewInformation3D.getExtendedInformationSequence()); - updateViewInformation(aNewViewInformation3D); + setViewInformation3D(aNewViewInformation3D); // #i102956# remember needed back-transform for found cuts (combine from right side) const basegfx::B3DHomMatrix aLastCombinedTransform(maCombinedTransform); @@ -87,7 +85,7 @@ namespace drawinglayer::processor3d // restore transformations and front, back maCombinedTransform = aLastCombinedTransform; - updateViewInformation(aLastViewInformation3D); + setViewInformation3D(aLastViewInformation3D); maFront = aLastFront; maBack = aLastBack; break; diff --git a/drawinglayer/source/processor3d/defaultprocessor3d.cxx b/drawinglayer/source/processor3d/defaultprocessor3d.cxx index 51dfefb6ac5d..1e1212470a56 100644 --- a/drawinglayer/source/processor3d/defaultprocessor3d.cxx +++ b/drawinglayer/source/processor3d/defaultprocessor3d.cxx @@ -31,7 +31,7 @@ #include <com/sun/star/drawing/ShadeMode.hpp> #include <drawinglayer/primitive3d/transformprimitive3d.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> -#include <vcl/bitmapex.hxx> +#include <vcl/bitmap.hxx> #include <drawinglayer/attribute/sdrsceneattribute3d.hxx> #include <drawinglayer/attribute/sdrlightingattribute3d.hxx> #include <vcl/graph.hxx> @@ -59,106 +59,82 @@ namespace drawinglayer::processor3d // create texture const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getGradient(); const basegfx::B2DRange aOutlineRange(0.0, 0.0, rPrimitive.getTextureSize().getX(), rPrimitive.getTextureSize().getY()); - const attribute::GradientStyle aGradientStyle(rFillGradient.getStyle()); - sal_uInt32 nSteps(rFillGradient.getSteps()); - const basegfx::BColor& aStart(rFillGradient.getStartColor()); - const basegfx::BColor& aEnd(rFillGradient.getEndColor()); - const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); + const css::awt::GradientStyle aGradientStyle(rFillGradient.getStyle()); std::shared_ptr< texture::GeoTexSvx > pNewTex; + basegfx::BColor aSingleColor; - if(nMaxSteps) + if (!rFillGradient.getColorStops().isSingleColor(aSingleColor)) { - // there IS a color distance - if(nSteps == 0) - { - nSteps = nMaxSteps; - } - - if(nSteps < 2) - { - nSteps = 2; - } - - if(nSteps > nMaxSteps) - { - nSteps = nMaxSteps; - } - switch(aGradientStyle) { - case attribute::GradientStyle::Linear: + default: // GradientStyle_MAKE_FIXED_SIZE + case css::awt::GradientStyle_LINEAR: { pNewTex = std::make_shared<texture::GeoTexSvxGradientLinear>( aOutlineRange, aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getAngle()); break; } - case attribute::GradientStyle::Axial: + case css::awt::GradientStyle_AXIAL: { pNewTex = std::make_shared<texture::GeoTexSvxGradientAxial>( aOutlineRange, aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getAngle()); break; } - case attribute::GradientStyle::Radial: + case css::awt::GradientStyle_RADIAL: { pNewTex = std::make_shared<texture::GeoTexSvxGradientRadial>( aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY()); break; } - case attribute::GradientStyle::Elliptical: + case css::awt::GradientStyle_ELLIPTICAL: { pNewTex = std::make_shared<texture::GeoTexSvxGradientElliptical>( aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), rFillGradient.getAngle()); break; } - case attribute::GradientStyle::Square: + case css::awt::GradientStyle_SQUARE: { pNewTex = std::make_shared<texture::GeoTexSvxGradientSquare>( aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), rFillGradient.getAngle()); break; } - case attribute::GradientStyle::Rect: + case css::awt::GradientStyle_RECT: { pNewTex = std::make_shared<texture::GeoTexSvxGradientRect>( aOutlineRange, - aStart, - aEnd, - nSteps, + rFillGradient.getSteps(), + rFillGradient.getColorStops(), rFillGradient.getBorder(), rFillGradient.getOffsetX(), rFillGradient.getOffsetY(), @@ -171,20 +147,16 @@ namespace drawinglayer::processor3d } else { - // no color distance -> same color, use simple texture - pNewTex = std::make_shared<texture::GeoTexSvxMono>(aStart, 1.0 - aStart.luminance()); + // only one color, so no real gradient -> use simple texture + pNewTex = std::make_shared<texture::GeoTexSvxMono>(aSingleColor, 1.0 - aSingleColor.luminance()); mbSimpleTextureActive = true; } // set created texture if(bTransparence) - { - mpTransparenceGeoTexSvx = pNewTex; - } + mpTransparenceGeoTexSvx = std::move(pNewTex); else - { - mpGeoTexSvx = pNewTex; - } + mpGeoTexSvx = std::move(pNewTex); // process sub-list process(rSubSequence); @@ -196,11 +168,11 @@ namespace drawinglayer::processor3d if(bTransparence) { - mpTransparenceGeoTexSvx = pOldTex; + mpTransparenceGeoTexSvx = std::move(pOldTex); } else { - mpGeoTexSvx = pOldTex; + mpGeoTexSvx = std::move(pOldTex); } } @@ -214,7 +186,7 @@ namespace drawinglayer::processor3d // rescue values const bool bOldModulate(getModulate()); mbModulate = rPrimitive.getModulate(); const bool bOldFilter(getFilter()); mbFilter = rPrimitive.getFilter(); - std::shared_ptr< texture::GeoTexSvx > pOldTex = mpGeoTexSvx; + std::shared_ptr<texture::GeoTexSvx> xOldTex(mpGeoTexSvx); // calculate logic pixel size in object coordinates. Create transformation view // to object by inverting ObjectToView @@ -242,7 +214,7 @@ namespace drawinglayer::processor3d // restore values mbModulate = bOldModulate; mbFilter = bOldFilter; - mpGeoTexSvx = pOldTex; + mpGeoTexSvx = std::move(xOldTex); } void DefaultProcessor3D::impRenderBitmapTexturePrimitive3D(const primitive3d::BitmapTexturePrimitive3D& rPrimitive) @@ -261,7 +233,7 @@ namespace drawinglayer::processor3d const attribute::FillGraphicAttribute& rFillGraphicAttribute = rPrimitive.getFillGraphicAttribute(); // #121194# For 3D texture we will use the BitmapRex representation - const BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); + const Bitmap aBitmap(rFillGraphicAttribute.getGraphic().GetBitmap()); // create range scaled by texture size basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); @@ -274,7 +246,7 @@ namespace drawinglayer::processor3d { mpGeoTexSvx = std::make_shared<texture::GeoTexSvxBitmapExTiled>( - aBitmapEx, + aBitmap, aGraphicRange, rFillGraphicAttribute.getOffsetX(), rFillGraphicAttribute.getOffsetY()); @@ -283,7 +255,7 @@ namespace drawinglayer::processor3d { mpGeoTexSvx = std::make_shared<texture::GeoTexSvxBitmapEx>( - aBitmapEx, + aBitmap, aGraphicRange); } @@ -293,7 +265,7 @@ namespace drawinglayer::processor3d // restore values mbModulate = bOldModulate; mbFilter = bOldFilter; - mpGeoTexSvx = pOldTex; + mpGeoTexSvx = std::move(pOldTex); } void DefaultProcessor3D::impRenderModifiedColorPrimitive3D(const primitive3d::ModifiedColorPrimitive3D& rModifiedCandidate) @@ -489,13 +461,13 @@ namespace drawinglayer::processor3d aLastViewInformation3D.getDeviceToView(), aLastViewInformation3D.getViewTime(), aLastViewInformation3D.getExtendedInformationSequence()); - updateViewInformation(aNewViewInformation3D); + setViewInformation3D(aNewViewInformation3D); // let break down recursively process(rTransformCandidate.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation3D); + setViewInformation3D(aLastViewInformation3D); } void DefaultProcessor3D::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rBasePrimitive) @@ -577,11 +549,7 @@ namespace drawinglayer::processor3d : BaseProcessor3D(rViewInformation), mrSdrSceneAttribute(rSdrSceneAttribute), mrSdrLightingAttribute(rSdrLightingAttribute), - maRasterRange(), maBColorModifierStack(), - mpGeoTexSvx(), - mpTransparenceGeoTexSvx(), - maDrawinglayerOpt(), mnTransparenceCounter(0), mbModulate(false), mbFilter(false), diff --git a/drawinglayer/source/processor3d/geometry2dextractor.cxx b/drawinglayer/source/processor3d/geometry2dextractor.cxx index c90eebe6819d..42144eae299e 100644 --- a/drawinglayer/source/processor3d/geometry2dextractor.cxx +++ b/drawinglayer/source/processor3d/geometry2dextractor.cxx @@ -23,14 +23,12 @@ #include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx> #include <drawinglayer/primitive3d/polygonprimitive3d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <primitive3d/textureprimitive3d.hxx> - - -using namespace com::sun::star; +#include <utility> namespace drawinglayer::processor3d @@ -56,13 +54,13 @@ namespace drawinglayer::processor3d aLastViewInformation3D.getDeviceToView(), aLastViewInformation3D.getViewTime(), aLastViewInformation3D.getExtendedInformationSequence()); - updateViewInformation(aNewViewInformation3D); + setViewInformation3D(aNewViewInformation3D); // let break down recursively process(rPrimitive.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation3D); + setViewInformation3D(aLastViewInformation3D); break; } case PRIMITIVE3D_ID_MODIFIEDCOLORPRIMITIVE3D : @@ -89,7 +87,7 @@ namespace drawinglayer::processor3d { a2DHairline.transform(getObjectTransformation()); const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); - const primitive2d::Primitive2DReference xRef(new primitive2d::PolygonHairlinePrimitive2D(a2DHairline, aModifiedColor)); + const primitive2d::Primitive2DReference xRef(new primitive2d::PolygonHairlinePrimitive2D(std::move(a2DHairline), aModifiedColor)); maPrimitive2DSequence.push_back(xRef); } break; @@ -104,7 +102,7 @@ namespace drawinglayer::processor3d { a2DFill.transform(getObjectTransformation()); const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(rPrimitive.getMaterial().getColor())); - const primitive2d::Primitive2DReference xRef(new primitive2d::PolyPolygonColorPrimitive2D(a2DFill, aModifiedColor)); + const primitive2d::Primitive2DReference xRef(new primitive2d::PolyPolygonColorPrimitive2D(std::move(a2DFill), aModifiedColor)); maPrimitive2DSequence.push_back(xRef); } break; @@ -141,10 +139,9 @@ namespace drawinglayer::processor3d Geometry2DExtractingProcessor::Geometry2DExtractingProcessor( const geometry::ViewInformation3D& rViewInformation, - const basegfx::B2DHomMatrix& rObjectTransformation) + basegfx::B2DHomMatrix aObjectTransformation) : BaseProcessor3D(rViewInformation), - maPrimitive2DSequence(), - maObjectTransformation(rObjectTransformation), + maObjectTransformation(std::move(aObjectTransformation)), maBColorModifierStack() { } diff --git a/drawinglayer/source/processor3d/shadow3dextractor.cxx b/drawinglayer/source/processor3d/shadow3dextractor.cxx index 0b653236eb1b..7970b46cfabb 100644 --- a/drawinglayer/source/processor3d/shadow3dextractor.cxx +++ b/drawinglayer/source/processor3d/shadow3dextractor.cxx @@ -24,11 +24,14 @@ #include <drawinglayer/primitive3d/transformprimitive3d.hxx> #include <drawinglayer/primitive3d/polygonprimitive3d.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> +#include <utility> using namespace com::sun::star; @@ -71,20 +74,19 @@ namespace drawinglayer::processor3d // create 2d shadow primitive with result. This also fetches all entries // from aNewSubList, so there is no need to delete them - primitive2d::BasePrimitive2D* pNew = new primitive2d::ShadowPrimitive2D( + rtl::Reference<primitive2d::BasePrimitive2D> pNew = new primitive2d::ShadowPrimitive2D( rPrimitive.getShadowTransform(), rPrimitive.getShadowColor(), 0, // shadow3d doesn't have rPrimitive.getShadowBlur() yet. - aNewSubList); + std::move(aNewSubList)); - if(basegfx::fTools::more(rPrimitive.getShadowTransparence(), 0.0)) + if(rPrimitive.getShadowTransparence() > 0.0) { // create simpleTransparencePrimitive, add created primitives - const primitive2d::Primitive2DReference xRef(pNew); - const primitive2d::Primitive2DContainer aNewTransPrimitiveVector { xRef }; + primitive2d::Primitive2DContainer aNewTransPrimitiveVector { pNew }; pNew = new primitive2d::UnifiedTransparencePrimitive2D( - aNewTransPrimitiveVector, + std::move(aNewTransPrimitiveVector), rPrimitive.getShadowTransparence()); } @@ -106,7 +108,7 @@ namespace drawinglayer::processor3d aLastViewInformation3D.getDeviceToView(), aLastViewInformation3D.getViewTime(), aLastViewInformation3D.getExtendedInformationSequence()); - updateViewInformation(aNewViewInformation3D); + setViewInformation3D(aNewViewInformation3D); if(mbShadowProjectionIsValid) { @@ -119,7 +121,7 @@ namespace drawinglayer::processor3d process(rPrimitive.getChildren()); // restore transformations - updateViewInformation(aLastViewInformation3D); + setViewInformation3D(aLastViewInformation3D); if(mbShadowProjectionIsValid) { @@ -154,7 +156,7 @@ namespace drawinglayer::processor3d a2DHairline.transform(getObjectTransformation()); mpPrimitive2DSequence->push_back( new primitive2d::PolygonHairlinePrimitive2D( - a2DHairline, + std::move(a2DHairline), basegfx::BColor())); } } @@ -185,7 +187,7 @@ namespace drawinglayer::processor3d a2DFill.transform(getObjectTransformation()); mpPrimitive2DSequence->push_back( new primitive2d::PolyPolygonColorPrimitive2D( - a2DFill, + std::move(a2DFill), basegfx::BColor())); } } @@ -202,19 +204,14 @@ namespace drawinglayer::processor3d Shadow3DExtractingProcessor::Shadow3DExtractingProcessor( const geometry::ViewInformation3D& rViewInformation, - const basegfx::B2DHomMatrix& rObjectTransformation, + basegfx::B2DHomMatrix aObjectTransformation, const basegfx::B3DVector& rLightNormal, double fShadowSlant, const basegfx::B3DRange& rContained3DRange) : BaseProcessor3D(rViewInformation), - maPrimitive2DSequence(), mpPrimitive2DSequence(&maPrimitive2DSequence), - maObjectTransformation(rObjectTransformation), - maWorldToEye(), - maEyeToView(), + maObjectTransformation(std::move(aObjectTransformation)), maLightNormal(rLightNormal), - maShadowPlaneNormal(), - maPlanePoint(), mfLightPlaneScalar(0.0), mbShadowProjectionIsValid(false), mbConvert(false), @@ -227,7 +224,7 @@ namespace drawinglayer::processor3d mfLightPlaneScalar = maLightNormal.scalar(maShadowPlaneNormal); // use only when scalar is > 0.0, so the light is in front of the object - if(!basegfx::fTools::more(mfLightPlaneScalar, 0.0)) + if(mfLightPlaneScalar <= 0.0 || basegfx::fTools::equalZero(mfLightPlaneScalar)) return; // prepare buffered WorldToEye and EyeToView diff --git a/drawinglayer/source/processor3d/zbufferprocessor3d.cxx b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx index 6cd65d60b416..22674b414cc6 100644 --- a/drawinglayer/source/processor3d/zbufferprocessor3d.cxx +++ b/drawinglayer/source/processor3d/zbufferprocessor3d.cxx @@ -27,6 +27,9 @@ #include <basegfx/polygon/b3dpolygontools.hxx> #include <basegfx/polygon/b3dpolypolygontools.hxx> #include <drawinglayer/attribute/sdrlightingattribute3d.hxx> +#include <o3tl/safeint.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <utility> using namespace com::sun::star; @@ -100,7 +103,7 @@ private: double decideColorAndOpacity(basegfx::BColor& rColor) const { // init values with full opacity and material color - OSL_ENSURE(nullptr != mpCurrentMaterial, "CurrentMaterial not set (!)"); + assert(nullptr != mpCurrentMaterial && "CurrentMaterial not set (!)"); double fOpacity(1.0); rColor = mpCurrentMaterial->getColor(); @@ -116,14 +119,14 @@ private: mrProcessor.getGeoTexSvx()->modifyBColor(aTexCoor, rColor, fOpacity); } - if(basegfx::fTools::more(fOpacity, 0.0) && mrProcessor.getTransparenceGeoTexSvx()) + if (fOpacity > 0.0 && !basegfx::fTools::equalZero(fOpacity) && mrProcessor.getTransparenceGeoTexSvx()) { // calc opacity. Object has a 2nd texture, a transparence texture mrProcessor.getTransparenceGeoTexSvx()->modifyOpacity(aTexCoor, fOpacity); } } - if(basegfx::fTools::more(fOpacity, 0.0)) + if (fOpacity > 0.0 && !basegfx::fTools::equalZero(fOpacity)) { if(mrProcessor.getGeoTexSvx()) { @@ -246,14 +249,8 @@ private: public: ZBufferRasterConverter3D(basegfx::BZPixelRaster& rBuffer, const drawinglayer::processor3d::ZBufferProcessor3D& rProcessor) - : basegfx::RasterConverter3D(), - mrProcessor(rProcessor), + : mrProcessor(rProcessor), mrBuffer(rBuffer), - maIntZ(), - maIntColor(), - maIntNormal(), - maIntTexture(), - maIntInvTexture(), mpCurrentMaterial(nullptr), mbModifyColor(false), mbUseTex(false), @@ -274,7 +271,7 @@ void ZBufferRasterConverter3D::processLineSpan(const basegfx::RasterConversionLi if(nSpanCount & 0x0001) return; - if(nLine < 0 || nLine >= static_cast<sal_Int32>(mrBuffer.getHeight())) + if(nLine < 0 || o3tl::make_unsigned(nLine) >= mrBuffer.getHeight()) return; sal_uInt32 nXA(std::min(mrBuffer.getWidth(), static_cast<sal_uInt32>(std::max(sal_Int32(0), basegfx::fround(rA.getX().getVal()))))); @@ -298,7 +295,7 @@ void ZBufferRasterConverter3D::processLineSpan(const basegfx::RasterConversionLi while(nXA < nXB) { // early-test Z values if we need to do anything at all - const double fNewZ(std::max(0.0, std::min(double(0xffff), maIntZ.getVal()))); + const double fNewZ(std::clamp(maIntZ.getVal(), 0.0, 65535.0)); const sal_uInt16 nNewZ(static_cast< sal_uInt16 >(fNewZ)); sal_uInt16& rOldZ(mrBuffer.getZ(nScanlineIndex)); @@ -322,20 +319,20 @@ void ZBufferRasterConverter3D::processLineSpan(const basegfx::RasterConversionLi { basegfx::BPixel& rDest = mrBuffer.getBPixel(nScanlineIndex); - if(rDest.getOpacity()) + if(rDest.getAlpha()) { // mix new color by using // color' = color * (1 - opacity) + newcolor * opacity - const sal_uInt16 nTransparence(0x0100 - nOpacity); + const sal_uInt16 nTransparence(255 - nOpacity); rDest.setRed(static_cast<sal_uInt8>(((rDest.getRed() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getRed()) * nOpacity)) >> 8)); rDest.setGreen(static_cast<sal_uInt8>(((rDest.getGreen() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getGreen()) * nOpacity)) >> 8)); rDest.setBlue(static_cast<sal_uInt8>(((rDest.getBlue() * nTransparence) + (static_cast<sal_uInt16>(255.0 * aNewColor.getBlue()) * nOpacity)) >> 8)); - if(0xff != rDest.getOpacity()) + if(255 != rDest.getAlpha()) { // both are transparent, mix new opacity by using // opacity = newopacity * (1 - oldopacity) + oldopacity - rDest.setOpacity(static_cast<sal_uInt8>((nOpacity * (0x0100 - rDest.getOpacity())) >> 8) + rDest.getOpacity()); + rDest.setAlpha(static_cast<sal_uInt8>((nOpacity * (255 - rDest.getAlpha())) >> 8) + rDest.getAlpha()); } } else @@ -379,16 +376,16 @@ private: public: RasterPrimitive3D( - const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pGeoTexSvx, - const std::shared_ptr< drawinglayer::texture::GeoTexSvx >& pTransparenceGeoTexSvx, + std::shared_ptr< drawinglayer::texture::GeoTexSvx > pGeoTexSvx, + std::shared_ptr< drawinglayer::texture::GeoTexSvx > pTransparenceGeoTexSvx, const drawinglayer::attribute::MaterialAttribute3D& rMaterial, const basegfx::B3DPolyPolygon& rPolyPolygon, bool bModulate, bool bFilter, bool bSimpleTextureActive, bool bIsLine) - : mpGeoTexSvx(pGeoTexSvx), - mpTransparenceGeoTexSvx(pTransparenceGeoTexSvx), + : mpGeoTexSvx(std::move(pGeoTexSvx)), + mpTransparenceGeoTexSvx(std::move(pTransparenceGeoTexSvx)), maMaterial(rMaterial), maPolyPolygon(rPolyPolygon), mfCenterZ(basegfx::utils::getRange(rPolyPolygon).getCenter().getZ()), @@ -422,12 +419,8 @@ namespace drawinglayer::processor3d { // transparent output; record for later sorting and painting from // back to front - if(!mpRasterPrimitive3Ds) - { - const_cast< ZBufferProcessor3D* >(this)->mpRasterPrimitive3Ds.reset( new std::vector< RasterPrimitive3D > ); - } - mpRasterPrimitive3Ds->push_back(RasterPrimitive3D( + maRasterPrimitive3Ds.push_back(RasterPrimitive3D( getGeoTexSvx(), getTransparenceGeoTexSvx(), rMaterial, @@ -444,7 +437,7 @@ namespace drawinglayer::processor3d if(mnAntiAlialize > 1) { - const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); + const bool bForceLineSnap(SvtOptionsDrawinglayer::IsAntiAliasing() && SvtOptionsDrawinglayer::IsSnapHorVerLinesToDiscrete()); if(bForceLineSnap) { @@ -486,12 +479,7 @@ namespace drawinglayer::processor3d { // transparent output; record for later sorting and painting from // back to front - if(!mpRasterPrimitive3Ds) - { - const_cast< ZBufferProcessor3D* >(this)->mpRasterPrimitive3Ds.reset( new std::vector< RasterPrimitive3D > ); - } - - mpRasterPrimitive3Ds->push_back(RasterPrimitive3D( + maRasterPrimitive3Ds.push_back(RasterPrimitive3D( getGeoTexSvx(), getTransparenceGeoTexSvx(), rMaterial, @@ -520,7 +508,6 @@ namespace drawinglayer::processor3d sal_uInt32 nStartLine, sal_uInt32 nStopLine) : DefaultProcessor3D(rViewInformation3D, rSdrSceneAttribute, rSdrLightingAttribute), - maInvEyeToView(), mnAntiAlialize(nAntiAlialize), mnStartLine(nStartLine), mnStopLine(nStopLine) @@ -575,7 +562,7 @@ namespace drawinglayer::processor3d aDeviceToView, getViewInformation3D().getViewTime(), getViewInformation3D().getExtendedInformationSequence()); - updateViewInformation(aNewViewInformation3D); + setViewInformation3D(aNewViewInformation3D); // prepare inverse EyeToView transformation. This can be done in constructor // since changes in object transformations when processing TransformPrimitive3Ds @@ -596,32 +583,31 @@ namespace drawinglayer::processor3d { mpZBufferRasterConverter3D.reset(); - if(mpRasterPrimitive3Ds) + if(!maRasterPrimitive3Ds.empty()) { OSL_FAIL("ZBufferProcessor3D: destructed, but there are unrendered transparent geometries. Use ZBufferProcessor3D::finish() to render these (!)"); } - mpRasterPrimitive3Ds.reset(); } void ZBufferProcessor3D::finish() { - if(!mpRasterPrimitive3Ds) + if(maRasterPrimitive3Ds.empty()) return; // there are transparent rasterprimitives - const sal_uInt32 nSize(mpRasterPrimitive3Ds->size()); + const sal_uInt32 nSize(maRasterPrimitive3Ds.size()); if(nSize > 1) { // sort them from back to front - std::sort(mpRasterPrimitive3Ds->begin(), mpRasterPrimitive3Ds->end()); + std::sort(maRasterPrimitive3Ds.begin(), maRasterPrimitive3Ds.end()); } for(sal_uInt32 a(0); a < nSize; a++) { // paint each one by setting the remembered data and calling // the render method - const RasterPrimitive3D& rCandidate = (*mpRasterPrimitive3Ds)[a]; + const RasterPrimitive3D& rCandidate = maRasterPrimitive3Ds[a]; mpGeoTexSvx = rCandidate.getGeoTexSvx(); mpTransparenceGeoTexSvx = rCandidate.getTransparenceGeoTexSvx(); @@ -645,7 +631,7 @@ namespace drawinglayer::processor3d // delete them to signal the destructor that all is done and // to allow asserting there - mpRasterPrimitive3Ds.reset(); + maRasterPrimitive3Ds.clear(); } } // end of namespace diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx index a7e9dca54c79..d0210dbc7b24 100644 --- a/drawinglayer/source/texture/texture.cxx +++ b/drawinglayer/source/texture/texture.cxx @@ -20,6 +20,7 @@ #include <sal/config.h> #include <algorithm> +#include <limits> #include <texture/texture.hxx> #include <basegfx/numeric/ftools.hxx> @@ -71,15 +72,14 @@ namespace drawinglayer::texture GeoTexSvxGradient::GeoTexSvxGradient( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder) - : GeoTexSvx(), - maGradientInfo(), - maDefinitionRange(rDefinitionRange), - maStart(rStart), - maEnd(rEnd), - mfBorder(fBorder) + : maDefinitionRange(rDefinitionRange) + , mnRequestedSteps(nRequestedSteps) + , mnColorStops(rColorStops) + , mfBorder(fBorder) + , maLastColorStopRange() { } @@ -94,26 +94,26 @@ namespace drawinglayer::texture return (pCompare && maGradientInfo == pCompare->maGradientInfo && maDefinitionRange == pCompare->maDefinitionRange + && mnRequestedSteps == pCompare->mnRequestedSteps + && mnColorStops == pCompare->mnColorStops && mfBorder == pCompare->mfBorder); } - GeoTexSvxGradientLinear::GeoTexSvxGradientLinear( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder), - mfUnitMinX(0.0), - mfUnitWidth(1.0), - mfUnitMaxY(1.0) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) + , mfUnitMinX(0.0) + , mfUnitWidth(1.0) + , mfUnitMaxY(1.0) { maGradientInfo = basegfx::utils::createLinearODFGradientInfo( rDefinitionRange, - nSteps, + nRequestedSteps, fBorder, fAngle); @@ -133,16 +133,30 @@ namespace drawinglayer::texture } void GeoTexSvxGradientLinear::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStripeWidth(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); + + if (bPenultimateUsed) + { + // Here we need to temporarily add a ColorStop entry with the + // same color as the last entry to correctly 'close' the + // created gradient geometry. + // The simplest way is to temporarily add an entry to the local + // ColorStops for this at 1.0 (using same color) + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // prepare unit range transform basegfx::B2DHomMatrix aPattern; // bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] @@ -153,54 +167,106 @@ namespace drawinglayer::texture aPattern.scale(mfUnitWidth, 1.0); aPattern.translate(mfUnitMinX, 0.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - const double fPos(fStripeWidth * a); - basegfx::B2DHomMatrix aNew(aPattern); - - // scale and translate in Y - double fHeight(1.0 - fPos); - - if(a + 1 == maGradientInfo.getSteps() && mfUnitMaxY > 1.0) + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + // nSteps is >= 1, see getRequestedSteps, so no check needed here + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // for the 1st color range we do not need to create the 1st step + // since it will be equal to StartColor and thus OuterColor, so + // will be painted by the 1st, always-created background polygon + // colored using OuterColor. + // We *need* to create this though for all 'inner' color ranges + // to get a correct start + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) { - fHeight += mfUnitMaxY - 1.0; + // calculate pos in Y + const double fPos(fOffsetStart + (fStripeWidth * innerLoop)); + + // scale and translate in Y. For GradientLinear we always have + // the full height + double fHeight(1.0 - fPos); + + if (mfUnitMaxY > 1.0) + { + // extend when difference between definition and OutputRange exists + fHeight += mfUnitMaxY - 1.0; + } + + basegfx::B2DHomMatrix aNew(aPattern); + aNew.scale(1.0, fHeight); + aNew.translate(0.0, fPos); + + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() * aNew, + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); } + } - aNew.scale(1.0, fHeight); - aNew.translate(0.0, fPos); - - // set at target - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew; - - // interpolate and set color - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - - rEntries.push_back(aB2DHomMatrixAndBColor); + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientAxial::GeoTexSvxGradientAxial( const basegfx::B2DRange& rDefinitionRange, const basegfx::B2DRange& rOutputRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder), - mfUnitMinX(0.0), - mfUnitWidth(1.0) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) + , mfUnitMinX(0.0) + , mfUnitWidth(1.0) { + // ARGH! GradientAxial uses the ColorStops in reverse order compared + // with the other gradients. Either stay 'thinking reverse' for the + // rest of time or adapt it here and go in same order as the other five, + // so unifications/tooling will be possible + mnColorStops.reverseColorStops(); + maGradientInfo = basegfx::utils::createAxialODFGradientInfo( rDefinitionRange, - nSteps, + nRequestedSteps, fBorder, fAngle); @@ -219,65 +285,118 @@ namespace drawinglayer::texture } void GeoTexSvxGradientAxial::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maEnd; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStripeWidth(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); + + if (bPenultimateUsed) + { + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // prepare unit range transform + basegfx::B2DHomMatrix aPattern; + + // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] + aPattern.scale(0.5, 1.0); + aPattern.translate(0.5, 0.0); + + // scale/translate in X + aPattern.scale(mfUnitWidth, 1.0); + aPattern.translate(mfUnitMinX, 0.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - const double fPos(fStripeWidth * a); - basegfx::B2DHomMatrix aNew; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); - // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1] - aNew.scale(0.5, 1.0); - aNew.translate(0.5, 0.0); + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; - // scale/translate in X - aNew.scale(mfUnitWidth, 1.0); - aNew.translate(mfUnitMinX, 0.0); + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); - // already centered in Y on X-Axis, just scale in Y - aNew.scale(1.0, 1.0 - fPos); + // calculate StripeWidth + // nSteps is >= 1, see getRequestedSteps, so no check needed here + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); - // set at target - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * aNew; + // for the 1st color range we do not need to create the 1st step, see above + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate pos in Y + const double fPos(fOffsetStart + (fStripeWidth * innerLoop)); - // interpolate and set color - aB2DHomMatrixAndBColor.maBColor = interpolate(maEnd, maStart, double(a) / double(maGradientInfo.getSteps() - 1)); + // already centered in Y on X-Axis, just scale in Y + basegfx::B2DHomMatrix aNew(aPattern); + aNew.scale(1.0, 1.0 - fPos); - rEntries.push_back(aB2DHomMatrixAndBColor); + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() * aNew, + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + // we use the reverse ColorSteps here, so use front value + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, maGradientInfo)); - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // we use the reverse ColorSteps here, so mirror scaler value + rBColor = mnColorStops.getInterpolatedBColor(1.0 - fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientRadial::GeoTexSvxGradientRadial( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createRadialODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder); } @@ -286,49 +405,100 @@ namespace drawinglayer::texture } void GeoTexSvxGradientRadial::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStepSize(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); + + if (bPenultimateUsed) + { + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) + { + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate size/radius + const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop))); + + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + if (bPenultimateUsed) { - const double fSize(1.0 - (fStepSize * a)); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -338,67 +508,107 @@ namespace drawinglayer::texture } void GeoTexSvxGradientElliptical::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - double fWidth(1.0); - double fHeight(1.0); - double fIncrementX(0.0); - double fIncrementY(0.0); + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - if(maGradientInfo.getAspectRatio() > 1.0) + if (bPenultimateUsed) { - fIncrementY = fHeight / maGradientInfo.getSteps(); - fIncrementX = fIncrementY / maGradientInfo.getAspectRatio(); - } - else - { - fIncrementX = fWidth / maGradientInfo.getSteps(); - fIncrementY = fIncrementX * maGradientInfo.getAspectRatio(); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); } - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // prepare vars dependent on aspect ratio + const double fAR(maGradientInfo.getAspectRatio()); + const bool bMTO(fAR > 1.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - // next step - fWidth -= fIncrementX; - fHeight -= fIncrementY; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate offset position for entry + const double fSize(fOffsetStart + (fStripeWidth * innerLoop)); + + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() + * basegfx::utils::createScaleB2DHomMatrix( + 1.0 - (bMTO ? fSize / fAR : fSize), + 1.0 - (bMTO ? fSize : fSize * fAR)), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientSquare::GeoTexSvxGradientSquare( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createSquareODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -408,49 +618,100 @@ namespace drawinglayer::texture } void GeoTexSvxGradientSquare::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - const double fStepSize(1.0 / maGradientInfo.getSteps()); - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + if (bPenultimateUsed) { - const double fSize(1.0 - (fStepSize * a)); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); + } + + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) + { + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); + + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate size/radius + const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop))); + + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; + + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } GeoTexSvxGradientRect::GeoTexSvxGradientRect( const basegfx::B2DRange& rDefinitionRange, - const basegfx::BColor& rStart, - const basegfx::BColor& rEnd, - sal_uInt32 nSteps, + sal_uInt32 nRequestedSteps, + const basegfx::BColorStops& rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle) - : GeoTexSvxGradient(rDefinitionRange, rStart, rEnd, fBorder) + : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder) { maGradientInfo = basegfx::utils::createRectangularODFGradientInfo( rDefinitionRange, basegfx::B2DVector(fOffsetX,fOffsetY), - nSteps, + nRequestedSteps, fBorder, fAngle); } @@ -460,49 +721,90 @@ namespace drawinglayer::texture } void GeoTexSvxGradientRect::appendTransformationsAndColors( - std::vector< B2DHomMatrixAndBColor >& rEntries, - basegfx::BColor& rOuterColor) + const std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)>& rCallback) { - rOuterColor = maStart; + // no color at all, done + if (mnColorStops.empty()) + return; - if(!maGradientInfo.getSteps()) + // only one color, done + if (mnColorStops.size() < 2) return; - double fWidth(1.0); - double fHeight(1.0); - double fIncrementX(0.0); - double fIncrementY(0.0); + // check if we need last-ColorStop-correction + const bool bPenultimateUsed(mnColorStops.checkPenultimate()); - if(maGradientInfo.getAspectRatio() > 1.0) - { - fIncrementY = fHeight / maGradientInfo.getSteps(); - fIncrementX = fIncrementY / maGradientInfo.getAspectRatio(); - } - else + if (bPenultimateUsed) { - fIncrementX = fWidth / maGradientInfo.getSteps(); - fIncrementY = fIncrementX * maGradientInfo.getAspectRatio(); + // temporarily add a ColorStop entry + mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor()); } - B2DHomMatrixAndBColor aB2DHomMatrixAndBColor; + // prepare vars dependent on aspect ratio + const double fAR(maGradientInfo.getAspectRatio()); + const bool bMTO(fAR > 1.0); - for(sal_uInt32 a(1); a < maGradientInfo.getSteps(); a++) + // outer loop over ColorStops, each is from cs_l to cs_r + for (auto cs_l(mnColorStops.begin()), cs_r(cs_l + 1); cs_r != mnColorStops.end(); cs_l++, cs_r++) { - // next step - fWidth -= fIncrementX; - fHeight -= fIncrementY; + // get offsets + const double fOffsetStart(cs_l->getStopOffset()); + const double fOffsetEnd(cs_r->getStopOffset()); + + // same offset, empty BColorStopRange, continue with next step + if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd)) + continue; + + // get colors & calculate steps + const basegfx::BColor aCStart(cs_l->getStopColor()); + const basegfx::BColor aCEnd(cs_r->getStopColor()); + const sal_uInt32 nSteps(basegfx::utils::calculateNumberOfSteps( + maGradientInfo.getRequestedSteps(), aCStart, aCEnd)); + + // calculate StripeWidth + const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps); - aB2DHomMatrixAndBColor.maB2DHomMatrix = maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fWidth, fHeight); - aB2DHomMatrixAndBColor.maBColor = interpolate(maStart, maEnd, double(a) / double(maGradientInfo.getSteps() - 1)); - rEntries.push_back(aB2DHomMatrixAndBColor); + // get correct start for inner loop (see above) + const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0); + + for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++) + { + // calculate offset position for entry + const double fSize(fOffsetStart + (fStripeWidth * innerLoop)); + + // set and add at target + rCallback( + maGradientInfo.getTextureTransform() + * basegfx::utils::createScaleB2DHomMatrix( + 1.0 - (bMTO ? fSize / fAR : fSize), + 1.0 - (bMTO ? fSize : fSize * fAR)), + 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1)))); + } + } + + if (bPenultimateUsed) + { + // correct temporary change + mnColorStops.pop_back(); } } void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const { - const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo)); + // no color at all, done + if (mnColorStops.empty()) + return; - rBColor = basegfx::interpolate(maStart, maEnd, fScaler); + // just single color, done + if (mnColorStops.size() < 2) + { + rBColor = mnColorStops.front().getStopColor(); + return; + } + + // texture-back-transform X/Y -> t [0.0..1.0] and determine color + const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV, maGradientInfo)); + rBColor = mnColorStops.getInterpolatedBColor(fScaler, mnRequestedSteps, maLastColorStopRange); } @@ -512,8 +814,6 @@ namespace drawinglayer::texture double fDistance, double fAngle) : maOutputRange(rOutputRange), - maTextureTransform(), - maBackTextureTransform(), mfDistance(0.1), mfAngle(fAngle), mnSteps(10), @@ -631,8 +931,20 @@ namespace drawinglayer::texture double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint& rUV) const { - const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV); - return fmod(aCoor.getY(), mfDistance); + // the below is an inlined and optimised version of + // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV); + // return fmod(aCoor.getY(), mfDistance); + + const basegfx::B2DHomMatrix& rMat = getBackTextureTransform(); + double fX = rUV.getX(); + double fY = rUV.getY(); + + double fTempY( + rMat.get(1, 0) * fX + + rMat.get(1, 1) * fY + + rMat.get(1, 2)); + + return fmod(fTempY, mfDistance); } const basegfx::B2DHomMatrix& GeoTexSvxHatch::getBackTextureTransform() const @@ -712,7 +1024,7 @@ namespace drawinglayer::texture sal_Int32 nPosX(0); sal_Int32 nPosY(0); - if(basegfx::fTools::more(fStartX, 0.0)) + if(fStartX > 0.0) { const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1); @@ -720,7 +1032,7 @@ namespace drawinglayer::texture fStartX -= nDiff * fWidth; } - if(basegfx::fTools::less(fStartX + fWidth, 0.0)) + if((fStartX + fWidth) < 0.0) { const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartX / fWidth))); @@ -728,7 +1040,7 @@ namespace drawinglayer::texture fStartX += nDiff * fWidth; } - if(basegfx::fTools::more(fStartY, 0.0)) + if(fStartY > 0.0) { const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartY / fHeight)) + 1); @@ -736,7 +1048,7 @@ namespace drawinglayer::texture fStartY -= nDiff * fHeight; } - if(basegfx::fTools::less(fStartY + fHeight, 0.0)) + if((fStartY + fHeight) < 0.0) { const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartY / fHeight))); diff --git a/drawinglayer/source/texture/texture3d.cxx b/drawinglayer/source/texture/texture3d.cxx index 1ea09cb0fdd9..03af53afe09b 100644 --- a/drawinglayer/source/texture/texture3d.cxx +++ b/drawinglayer/source/texture/texture3d.cxx @@ -22,10 +22,11 @@ #include <algorithm> #include <texture/texture3d.hxx> -#include <vcl/bitmapaccess.hxx> +#include <vcl/BitmapReadAccess.hxx> #include <vcl/BitmapTools.hxx> #include <primitive3d/hatchtextureprimitive3d.hxx> #include <sal/log.hxx> +#include <osl/diagnose.h> namespace drawinglayer::texture { @@ -58,39 +59,16 @@ namespace drawinglayer::texture GeoTexSvxBitmapEx::GeoTexSvxBitmapEx( - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, const basegfx::B2DRange& rRange) - : maBitmapEx(rBitmapEx), - maTransparence(), + : maBitmap(rBitmap), maTopLeft(rRange.getMinimum()), maSize(rRange.getRange()), mfMulX(0.0), - mfMulY(0.0), - mbIsAlpha(false), - mbIsTransparent(maBitmapEx.IsTransparent()) + mfMulY(0.0) { - if(vcl::bitmap::convertBitmap32To24Plus8(maBitmapEx,maBitmapEx)) - mbIsTransparent = maBitmapEx.IsTransparent(); - // #121194# Todo: use alpha channel, too (for 3d) - maBitmap = maBitmapEx.GetBitmap(); - - if(mbIsTransparent) - { - if(maBitmapEx.IsAlpha()) - { - mbIsAlpha = true; - maTransparence = rBitmapEx.GetAlpha().GetBitmap(); - } - else - { - maTransparence = rBitmapEx.GetMask(); - } - - mpReadTransparence = Bitmap::ScopedReadAccess(maTransparence); - } - - if (!!maBitmap) - mpReadBitmap = Bitmap::ScopedReadAccess(maBitmap); + if (!maBitmap.IsEmpty()) + mpReadBitmap = maBitmap; SAL_WARN_IF(!mpReadBitmap, "drawinglayer", "GeoTexSvxBitmapEx: Got no read access to Bitmap"); if (mpReadBitmap) { @@ -113,48 +91,6 @@ namespace drawinglayer::texture { } - sal_uInt8 GeoTexSvxBitmapEx::impGetTransparence(sal_Int32 rX, sal_Int32 rY) const - { - switch(maBitmapEx.GetTransparentType()) - { - case TransparentType::NONE: - { - break; - } - case TransparentType::Color: - { - const BitmapColor aBitmapColor(mpReadBitmap->GetColor(rY, rX)); - - if(maBitmapEx.GetTransparentColor() == aBitmapColor) - { - return 255; - } - - break; - } - case TransparentType::Bitmap: - { - OSL_ENSURE(mpReadTransparence, "OOps, transparence type Bitmap, but no read access created in the constructor (?)"); - const BitmapColor aBitmapColor(mpReadTransparence->GetPixel(rY, rX)); - - if(mbIsAlpha) - { - return aBitmapColor.GetIndex(); - } - else - { - if(0x00 != aBitmapColor.GetIndex()) - { - return 255; - } - } - break; - } - } - - return 0; - } - bool GeoTexSvxBitmapEx::impIsValid(const basegfx::B2DPoint& rUV, sal_Int32& rX, sal_Int32& rY) const { if(mpReadBitmap) @@ -187,12 +123,12 @@ namespace drawinglayer::texture rBColor = aBSource; - if(mbIsTransparent) + if (maBitmap.HasAlpha()) { - // when we have a transparence, make use of it - const sal_uInt8 aLuminance(impGetTransparence(nX, nY)); + // when we have alpha, make use of it + const sal_uInt8 aAlpha(aBMCol.GetAlpha()); - rfOpacity = (static_cast<double>(0xff - aLuminance) * (1.0 / 255.0)); + rfOpacity = (static_cast<double>(aAlpha) * (1.0 / 255.0)); } else { @@ -211,18 +147,18 @@ namespace drawinglayer::texture if(impIsValid(rUV, nX, nY)) { - if(mbIsTransparent) + const BitmapColor aBMCol(mpReadBitmap->GetColor(nY, nX)); + if (maBitmap.HasAlpha()) { // this texture has an alpha part, use it - const sal_uInt8 aLuminance(impGetTransparence(nX, nY)); - const double fNewOpacity(static_cast<double>(0xff - aLuminance) * (1.0 / 255.0)); + const sal_uInt8 aAlpha(aBMCol.GetAlpha()); + const double fNewOpacity(static_cast<double>(aAlpha) * (1.0 / 255.0)); rfOpacity = 1.0 - ((1.0 - fNewOpacity) * (1.0 - rfOpacity)); } else { // this texture is a color bitmap used as transparence map - const BitmapColor aBMCol(mpReadBitmap->GetColor(nY, nX)); const Color aColor(aBMCol.GetRed(), aBMCol.GetGreen(), aBMCol.GetBlue()); rfOpacity = (static_cast<double>(0xff - aColor.GetLuminance()) * (1.0 / 255.0)); @@ -276,11 +212,11 @@ namespace drawinglayer::texture } GeoTexSvxBitmapExTiled::GeoTexSvxBitmapExTiled( - const BitmapEx& rBitmapEx, + const Bitmap& rBitmap, const basegfx::B2DRange& rRange, double fOffsetX, double fOffsetY) - : GeoTexSvxBitmapEx(rBitmapEx, rRange), + : GeoTexSvxBitmapEx(rBitmap, rRange), mfOffsetX(std::clamp(fOffsetX, 0.0, 1.0)), mfOffsetY(std::clamp(fOffsetY, 0.0, 1.0)), mbUseOffsetX(!basegfx::fTools::equalZero(mfOffsetX)), @@ -327,7 +263,7 @@ namespace drawinglayer::texture aOutlineRange, aOutlineRange, rHatch.getDistance(), - fAngleA + F_PI2) ); + fAngleA + M_PI_2) ); } if(attribute::HatchStyle::Triple == rHatch.getStyle()) @@ -336,7 +272,7 @@ namespace drawinglayer::texture aOutlineRange, aOutlineRange, rHatch.getDistance(), - fAngleA + F_PI4) ); + fAngleA + M_PI_4) ); } } diff --git a/drawinglayer/source/tools/converters.cxx b/drawinglayer/source/tools/converters.cxx index ff9b79a688fa..fc6bef06fd21 100644 --- a/drawinglayer/source/tools/converters.cxx +++ b/drawinglayer/source/tools/converters.cxx @@ -23,153 +23,379 @@ #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <drawinglayer/processor2d/baseprocessor2d.hxx> #include <drawinglayer/processor2d/processor2dtools.hxx> +#include <vcl/alpha.hxx> +#include <vcl/svapp.hxx> #include <vcl/virdev.hxx> +#include <com/sun/star/geometry/RealRectangle2D.hpp> +#include <comphelper/diagnose_ex.hxx> -#include <converters.hxx> +#include <drawinglayer/converters.hxx> +#include <config_vclplug.h> #ifdef DBG_UTIL +#include <o3tl/environment.hxx> #include <tools/stream.hxx> -#include <vcl/pngwrite.hxx> +#include <vcl/dibtools.hxx> #endif +namespace +{ +bool implPrepareConversion(drawinglayer::primitive2d::Primitive2DContainer& rSequence, + sal_uInt32& rnDiscreteWidth, sal_uInt32& rnDiscreteHeight, + const sal_uInt32 nMaxSquarePixels) +{ + if (rSequence.empty()) + return false; + + if (rnDiscreteWidth <= 0 || rnDiscreteHeight <= 0) + return false; + + const sal_uInt32 nViewVisibleArea(rnDiscreteWidth * rnDiscreteHeight); + + if (nViewVisibleArea > nMaxSquarePixels) + { + // reduce render size + double fReduceFactor + = sqrt(static_cast<double>(nMaxSquarePixels) / static_cast<double>(nViewVisibleArea)); + rnDiscreteWidth = basegfx::fround(static_cast<double>(rnDiscreteWidth) * fReduceFactor); + rnDiscreteHeight = basegfx::fround(static_cast<double>(rnDiscreteHeight) * fReduceFactor); + + const drawinglayer::primitive2d::Primitive2DReference aEmbed( + new drawinglayer::primitive2d::TransformPrimitive2D( + basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor), + std::move(rSequence))); + + rSequence = drawinglayer::primitive2d::Primitive2DContainer{ aEmbed }; + } + + return true; +} + +AlphaMask implcreateAlphaMask(drawinglayer::primitive2d::Primitive2DContainer& rSequence, + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, + const Size& rSizePixel, bool bUseLuminance) +{ + ScopedVclPtrInstance<VirtualDevice> pContent; + + // prepare vdev + if (!pContent->SetOutputSizePixel(rSizePixel, false)) + { + SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << rSizePixel.Width() << "x" + << rSizePixel.Height()); + return AlphaMask(); + } + + // create pixel processor, also already takes care of AAing and + // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If + // not wanted, change after this call as needed + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pContentProcessor + = drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(*pContent, + rViewInformation2D); + + // prepare for mask creation + pContent->SetMapMode(MapMode(MapUnit::MapPixel)); + + // set transparency to all white (fully transparent) + pContent->Erase(); + + basegfx::BColorModifierSharedPtr aBColorModifier; + if (bUseLuminance) + { + // new mode: bUseLuminance allows simple creation of alpha channels + // for any content (e.g. gradients) + aBColorModifier = std::make_shared<basegfx::BColorModifier_luminance_to_alpha>(); + } + else + { + // Embed primitives to paint them black (fully opaque) + aBColorModifier + = std::make_shared<basegfx::BColorModifier_replace>(basegfx::BColor(0.0, 0.0, 0.0)); + } + const drawinglayer::primitive2d::Primitive2DReference xRef( + new drawinglayer::primitive2d::ModifiedColorPrimitive2D(std::move(rSequence), + std::move(aBColorModifier))); + const drawinglayer::primitive2d::Primitive2DContainer xSeq{ xRef }; + + // render + pContentProcessor->process(xSeq); + pContentProcessor.reset(); + + // get alpha channel from vdev + pContent->EnableMapMode(false); + const Point aEmptyPoint; + + // Convert from transparency->alpha. + // FIXME in theory I should be able to directly construct alpha by using black as background + // and white as foreground, but that doesn't work for some reason. + Bitmap aContentBitmap = pContent->GetBitmap(aEmptyPoint, rSizePixel); + aContentBitmap.Invert(); + + return AlphaMask(aContentBitmap); +} +} + namespace drawinglayer { +AlphaMask createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer&& rSeq, + const geometry::ViewInformation2D& rViewInformation2D, + sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight, + sal_uInt32 nMaxSquarePixels, bool bUseLuminance) +{ + drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq)); - BitmapEx convertToBitmapEx( - const drawinglayer::primitive2d::Primitive2DContainer& rSeq, - const geometry::ViewInformation2D& rViewInformation2D, - sal_uInt32 nDiscreteWidth, - sal_uInt32 nDiscreteHeight, - sal_uInt32 nMaxQuadratPixels) + if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels)) { - BitmapEx aRetval; + return AlphaMask(); + } - if(!rSeq.empty() && nDiscreteWidth && nDiscreteHeight) - { - // get destination size in pixels - const MapMode aMapModePixel(MapUnit::MapPixel); - const sal_uInt32 nViewVisibleArea(nDiscreteWidth * nDiscreteHeight); - drawinglayer::primitive2d::Primitive2DContainer aSequence(rSeq); - - if(nViewVisibleArea > nMaxQuadratPixels) - { - // reduce render size - double fReduceFactor = sqrt(static_cast<double>(nMaxQuadratPixels) / static_cast<double>(nViewVisibleArea)); - nDiscreteWidth = basegfx::fround(static_cast<double>(nDiscreteWidth) * fReduceFactor); - nDiscreteHeight = basegfx::fround(static_cast<double>(nDiscreteHeight) * fReduceFactor); - - const drawinglayer::primitive2d::Primitive2DReference aEmbed( - new drawinglayer::primitive2d::TransformPrimitive2D( - basegfx::utils::createScaleB2DHomMatrix(fReduceFactor, fReduceFactor), - rSeq)); - - aSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed }; - } - - const Point aEmptyPoint; - const Size aSizePixel(nDiscreteWidth, nDiscreteHeight); - ScopedVclPtrInstance< VirtualDevice > pContent; - - // prepare vdev - pContent->SetOutputSizePixel(aSizePixel, false); - pContent->SetMapMode(aMapModePixel); - - // set to all white - pContent->SetBackground(Wallpaper(COL_WHITE)); - pContent->Erase(); - - // create pixel processor, also already takes care of AAing and - // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If - // not wanted, change after this call as needed - std::unique_ptr<processor2d::BaseProcessor2D> pContentProcessor = processor2d::createPixelProcessor2DFromOutputDevice( - *pContent, - rViewInformation2D); - - if(pContentProcessor) - { -#ifdef DBG_UTIL - static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore -#endif - // render content - pContentProcessor->process(aSequence); + const Size aSizePixel(nDiscreteWidth, nDiscreteHeight); - // get content - pContent->EnableMapMode(false); - const Bitmap aContent(pContent->GetBitmap(aEmptyPoint, aSizePixel)); + return implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, bUseLuminance); +} -#ifdef DBG_UTIL - if(bDoSaveForVisualControl) - { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\test_content.png" -#else - "~/test_content.png" -#endif - , StreamMode::WRITE|StreamMode::TRUNC); - BitmapEx aContentEx(aContent); - vcl::PNGWriter aPNGWriter(aContentEx); - aPNGWriter.Write(aNew); - } +Bitmap convertToBitmap(drawinglayer::primitive2d::Primitive2DContainer&& rSeq, + const geometry::ViewInformation2D& rViewInformation2D, + sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight, + sal_uInt32 nMaxSquarePixels, bool bForceAlphaMaskCreation) +{ + drawinglayer::primitive2d::Primitive2DContainer aSequence(std::move(rSeq)); + + if (!implPrepareConversion(aSequence, nDiscreteWidth, nDiscreteHeight, nMaxSquarePixels)) + { + return Bitmap(); + } + +#if USE_HEADLESS_CODE + // shortcut: try to directly create a PixelProcessor2D with + // RGBA support - that's what we need + // Currently only implemented for CairoSDPR, so add code only + // for USE_HEADLESS_CODE, but is designed as a general functionality + std::unique_ptr<processor2d::BaseProcessor2D> pRGBAProcessor + = processor2d::createPixelProcessor2DFromScratch(rViewInformation2D, nDiscreteWidth, nDiscreteHeight, true); + if (pRGBAProcessor) + { + // render content + pRGBAProcessor->process(aSequence); + + // create final Bitmap result (content) + const Bitmap aRetval(processor2d::extractBitmapFromBaseProcessor2D(pRGBAProcessor)); + + // check if we have a result and return if so + if (!aRetval.IsEmpty()) + return aRetval; + } #endif - // prepare for mask creation - pContent->SetMapMode(aMapModePixel); - - // set alpha to all white (fully transparent) - pContent->Erase(); - - // embed primitives to paint them black - const primitive2d::Primitive2DReference xRef( - new primitive2d::ModifiedColorPrimitive2D( - aSequence, - std::make_shared<basegfx::BColorModifier_replace>( - basegfx::BColor(0.0, 0.0, 0.0)))); - const primitive2d::Primitive2DContainer xSeq { xRef }; - - // render - pContentProcessor->process(xSeq); - pContentProcessor.reset(); - - // get alpha channel from vdev - pContent->EnableMapMode(false); - const Bitmap aAlpha(pContent->GetBitmap(aEmptyPoint, aSizePixel)); + + const Point aEmptyPoint; + const Size aSizePixel(nDiscreteWidth, nDiscreteHeight); + + // Create target VirtualDevice. Go back to using a simple RGB + // target version (compared with former version, see history). + // Reasons are manyfold: + // - Avoid the RGBA mode for VirtualDevice (two VDevs) + // - It's not suggested to be used outside presentation engine + // - It only works *by chance* with VCLPrimitiveRenderer + // - Usage of two-VDev alpha-VDev avoided alpha blending against + // COL_WHITE in the 1st layer of targets (not in buffers below) + // but is kind of a 'hack' doing so + // - Other renderers (system-dependent PrimitiveRenderers, other + // than the VCL-based ones) will probably not support splitted + // VDevs for content/alpha, so require a method that works with + // RGB targeting (for now) + // - Less resource usage, better speed (no 2 VDevs, no merge of + // AlphaChannels) + // As long as not all our mechanisms are changed to RGBA completely, + // mixing these is just too dangerous and expensive and may to wrong + // or deliver bad quality results. + // Nonetheless we need a RGBA result here. Luckily we are able to + // create a complete and valid AlphaChannel using 'createAlphaMask' + // above. + // When we know the content (RGB result from renderer), alpha + // (result from createAlphaMask) and the start condition (content + // rendered against COL_WHITE), it is possible to calculate back + // the content, quasi 'remove' that initial blending against + // COL_WHITE. + // That is what the helper Bitmap::RemoveBlendedStartColor does. + // Luckily we only need it for this 'convertToBitmapEx', not in + // any other rendering. It could be further optimized, too. + // This gives good results, it is in principle comparable with + // the results using pre-multiplied alpha tooling, also reducing + // the range of values where high alpha values are used. + ScopedVclPtrInstance<VirtualDevice> pContent(*Application::GetDefaultDevice()); + + // prepare vdev + if (!pContent->SetOutputSizePixel(aSizePixel, false)) + { + SAL_WARN("vcl", "Cannot set VirtualDevice to size : " << aSizePixel.Width() << "x" + << aSizePixel.Height()); + return Bitmap(); + } + + // We map to pixel, use that MapMode. Init by erasing. + pContent->SetMapMode(MapMode(MapUnit::MapPixel)); + pContent->Erase(); + + // create pixel processor, also already takes care of AAing and + // checking the getOptionsDrawinglayer().IsAntiAliasing() switch. If + // not wanted, change after this call as needed + std::unique_ptr<processor2d::BaseProcessor2D> pContentProcessor + = processor2d::createPixelProcessor2DFromOutputDevice(*pContent, rViewInformation2D); + + // render content + pContentProcessor->process(aSequence); + + // create final Bitmap result (content) + Bitmap aRetval(pContent->GetBitmap(aEmptyPoint, aSizePixel)); + #ifdef DBG_UTIL - if(bDoSaveForVisualControl) - { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\test_alpha.png" -#else - "~/test_alpha.png" -#endif - , StreamMode::WRITE|StreamMode::TRUNC); - BitmapEx aAlphaEx(aAlpha); - vcl::PNGWriter aPNGWriter(aAlphaEx); - aPNGWriter.Write(aNew); - } + static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore + if (bDoSaveForVisualControl) + { + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) + { + SvFileStream aNew(sDumpPath + "test_content.bmp", + StreamMode::WRITE | StreamMode::TRUNC); + WriteDIB(aRetval, aNew, false, true); + } + } #endif - // create BitmapEx result - aRetval = BitmapEx(aContent, AlphaMask(aAlpha)); + // Create the AlphaMask using a method that does this always correct (also used + // now in GlowPrimitive2D and ShadowPrimitive2D which both only need the + // AlphaMask to do their job, so speeding that up, too). + AlphaMask aAlpha(implcreateAlphaMask(aSequence, rViewInformation2D, aSizePixel, false)); + #ifdef DBG_UTIL - if(bDoSaveForVisualControl) - { - SvFileStream aNew( -#ifdef _WIN32 - "c:\\test_combined.png" -#else - "~/test_combined.png" -#endif - , StreamMode::WRITE|StreamMode::TRUNC); - vcl::PNGWriter aPNGWriter(aRetval); - aPNGWriter.Write(aNew); - } -#endif - } + if (bDoSaveForVisualControl) + { + // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/ + static const OUString sDumpPath(o3tl::getEnvironment(u"VCL_DUMP_BMP_PATH"_ustr)); + if (!sDumpPath.isEmpty()) + { + SvFileStream aNew(sDumpPath + "test_alpha.bmp", StreamMode::WRITE | StreamMode::TRUNC); + WriteDIB(aAlpha.GetBitmap(), aNew, false, true); } + } +#endif + if (bForceAlphaMaskCreation || aAlpha.hasAlpha()) + { + // Need to correct content using known alpha to get to background-free + // RGBA result, usable e.g. in PNG export(s) or convert-to-bitmap. + // Now that vcl supports bitmaps with an alpha channel, only apply + // this correction to bitmaps without an alpha channel. + if (pContent->GetBitCount() < 32) + { + aRetval.RemoveBlendedStartColor(COL_BLACK, aAlpha); + } + else + { + // tdf#157558 invert and remove blended white color + // Before commit 81994cb2b8b32453a92bcb011830fcb884f22ff3, + // RemoveBlendedStartColor(COL_BLACK, aAlpha) would darken + // the bitmap when running a slideshow, printing, or exporting + // to PDF. To get the same effect, the alpha mask must be + // inverted, RemoveBlendedStartColor(COL_WHITE, aAlpha) + // called, and the alpha mask uninverted. + aAlpha.Invert(); + aRetval.RemoveBlendedStartColor(COL_WHITE, aAlpha); + aAlpha.Invert(); + } + // return combined result + return Bitmap(aRetval, aAlpha); + } + else return aRetval; +} + +Bitmap convertPrimitive2DContainerToBitmap(primitive2d::Primitive2DContainer&& rSequence, + const basegfx::B2DRange& rTargetRange, + sal_uInt32 nMaximumQuadraticPixels, + const o3tl::Length eTargetUnit, + const std::optional<Size>& rTargetDPI) +{ + if (rSequence.empty()) + return Bitmap(); + + try + { + css::geometry::RealRectangle2D aRealRect; + aRealRect.X1 = rTargetRange.getMinX(); + aRealRect.Y1 = rTargetRange.getMinY(); + aRealRect.X2 = rTargetRange.getMaxX(); + aRealRect.Y2 = rTargetRange.getMaxY(); + + // get system DPI + Size aDPI( + Application::GetDefaultDevice()->LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch))); + if (rTargetDPI.has_value()) + { + aDPI = *rTargetDPI; + } + + ::sal_uInt32 DPI_X = aDPI.getWidth(); + ::sal_uInt32 DPI_Y = aDPI.getHeight(); + const basegfx::B2DRange aRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2); + const double fWidth(aRange.getWidth()); + const double fHeight(aRange.getHeight()); + + if (fWidth <= 0.0 || fHeight <= 0.0 || basegfx::fTools::equalZero(fWidth) || basegfx::fTools::equalZero(fHeight)) + return Bitmap(); + + if (0 == DPI_X) + { + DPI_X = 75; + } + + if (0 == DPI_Y) + { + DPI_Y = 75; + } + + if (0 == nMaximumQuadraticPixels) + { + nMaximumQuadraticPixels = 500000; + } + + const auto aViewInformation2D = geometry::createViewInformation2D({}); + const sal_uInt32 nDiscreteWidth( + basegfx::fround(o3tl::convert(fWidth, eTargetUnit, o3tl::Length::in) * DPI_X)); + const sal_uInt32 nDiscreteHeight( + basegfx::fround(o3tl::convert(fHeight, eTargetUnit, o3tl::Length::in) * DPI_Y)); + + basegfx::B2DHomMatrix aEmbedding( + basegfx::utils::createTranslateB2DHomMatrix(-aRange.getMinX(), -aRange.getMinY())); + + aEmbedding.scale(nDiscreteWidth / fWidth, nDiscreteHeight / fHeight); + + const primitive2d::Primitive2DReference xEmbedRef( + new primitive2d::TransformPrimitive2D(aEmbedding, std::move(rSequence))); + primitive2d::Primitive2DContainer xEmbedSeq{ xEmbedRef }; + + Bitmap aBitmap(convertToBitmap(std::move(xEmbedSeq), aViewInformation2D, + nDiscreteWidth, nDiscreteHeight, + nMaximumQuadraticPixels)); + + if (aBitmap.IsEmpty()) + return Bitmap(); + aBitmap.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + aBitmap.SetPrefSize(Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight))); + + return aBitmap; + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("vcl", "Got no graphic::XPrimitive2DRenderer!"); } + catch (const std::exception& e) + { + SAL_WARN("vcl", "Got no graphic::XPrimitive2DRenderer! : " << e.what()); + } + + return Bitmap(); +} } // end of namespace drawinglayer diff --git a/drawinglayer/source/tools/emfpbrush.cxx b/drawinglayer/source/tools/emfpbrush.cxx index ca0c27b23ecf..786209bb641b 100644 --- a/drawinglayer/source/tools/emfpbrush.cxx +++ b/drawinglayer/source/tools/emfpbrush.cxx @@ -52,13 +52,13 @@ namespace emfplushelper { switch (type) { - case BrushTypeSolidColor: return "BrushTypeSolidColor"; - case BrushTypeHatchFill: return "BrushTypeHatchFill"; - case BrushTypeTextureFill: return "BrushTypeTextureFill"; - case BrushTypePathGradient: return "BrushTypePathGradient"; - case BrushTypeLinearGradient: return "BrushTypeLinearGradient"; + case BrushTypeSolidColor: return u"BrushTypeSolidColor"_ustr; + case BrushTypeHatchFill: return u"BrushTypeHatchFill"_ustr; + case BrushTypeTextureFill: return u"BrushTypeTextureFill"_ustr; + case BrushTypePathGradient: return u"BrushTypePathGradient"_ustr; + case BrushTypeLinearGradient: return u"BrushTypeLinearGradient"_ustr; } - return ""; + return u""_ustr; } void EMFPBrush::Read(SvStream& s, EmfPlusHelperData const & rR) @@ -67,8 +67,8 @@ namespace emfplushelper s.ReadUInt32(header).ReadUInt32(type); - SAL_INFO("drawinglayer", "EMF+\t\t\tHeader: 0x" << std::hex << header); - SAL_INFO("drawinglayer", "EMF+\t\t\tType: " << BrushTypeToString(type) << "(0x" << type << ")" << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tHeader: 0x" << std::hex << header); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tType: " << BrushTypeToString(type) << "(0x" << type << ")" << std::dec); switch (type) { @@ -77,8 +77,8 @@ namespace emfplushelper sal_uInt32 color; s.ReadUInt32(color); - solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tSolid color: 0x" << std::hex << color << std::dec); + solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tSolid color: 0x" << std::hex << color << std::dec); break; } case BrushTypeHatchFill: @@ -91,53 +91,48 @@ namespace emfplushelper s.ReadUInt32(backgroundColor); hatchStyle = static_cast<EmfPlusHatchStyle>(style); - solidColor = ::Color(0xff - (foregroundColor >> 24), (foregroundColor >> 16) & 0xff, (foregroundColor >> 8) & 0xff, foregroundColor & 0xff); - secondColor = ::Color(0xff - (backgroundColor >> 24), (backgroundColor >> 16) & 0xff, (backgroundColor >> 8) & 0xff, backgroundColor & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tHatch style: 0x" << std::hex << style); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tForeground color: 0x" << solidColor.AsRGBHexString()); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tBackground color: 0x" << secondColor.AsRGBHexString()); + solidColor = ::Color(ColorAlpha, (foregroundColor >> 24), (foregroundColor >> 16) & 0xff, (foregroundColor >> 8) & 0xff, foregroundColor & 0xff); + secondColor = ::Color(ColorAlpha, (backgroundColor >> 24), (backgroundColor >> 16) & 0xff, (backgroundColor >> 8) & 0xff, backgroundColor & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tHatch style: 0x" << std::hex << style); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tForeground color: 0x" << solidColor.AsRGBHexString()); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tBackground color: 0x" << secondColor.AsRGBHexString()); break; } case BrushTypeTextureFill: { - SAL_WARN("drawinglayer", "EMF+\tTODO: implement BrushTypeTextureFill brush"); + SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush"); break; } case BrushTypePathGradient: { s.ReadUInt32(additionalFlags).ReadInt32(wrapMode); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tAdditional flags: 0x" << std::hex << additionalFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tAdditional flags: 0x" << std::hex << additionalFlags << std::dec); sal_uInt32 color; s.ReadUInt32(color); - solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tCenter color: 0x" << std::hex << color << std::dec); + solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter color: 0x" << std::hex << color << std::dec); s.ReadFloat(firstPointX).ReadFloat(firstPointY); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tCenter point: " << firstPointX << "," << firstPointY); - s.ReadInt32(surroundColorsNumber); - SAL_INFO("drawinglayer", "EMF+\t\t\t\t number of surround colors: " << surroundColorsNumber); - - if (surroundColorsNumber<0 || o3tl::make_unsigned(surroundColorsNumber)>SAL_MAX_INT32 / sizeof(::Color)) - { - surroundColorsNumber = SAL_MAX_INT32 / sizeof(::Color); - } + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tCenter point: " << firstPointX << "," << firstPointY); + s.ReadUInt32(surroundColorsNumber); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t number of surround colors: " << surroundColorsNumber); surroundColors.reset( new ::Color[surroundColorsNumber] ); - for (int i = 0; i < surroundColorsNumber; i++) + for (sal_uInt32 i = 0; i < surroundColorsNumber; i++) { s.ReadUInt32(color); - surroundColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + surroundColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); if (i == 0) secondColor = surroundColors[0]; - SAL_INFO("drawinglayer", "EMF+\t\t\t\tSurround color[" << i << "]: 0x" << std::hex << color << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tSurround color[" << i << "]: 0x" << std::hex << color << std::dec); } - if (additionalFlags & 0x01) + if (additionalFlags & 0x01) // BrushDataPath { sal_Int32 pathLength; s.ReadInt32(pathLength); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPath length: " << pathLength); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPath length: " << pathLength); sal_uInt64 const pos = s.Tell(); @@ -145,10 +140,10 @@ namespace emfplushelper sal_Int32 pathPoints, pathFlags; s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPath (brush path gradient)"); - SAL_INFO("drawinglayer", "EMF+\t\t\t\t\tHeader: 0x" << std::hex << pathHeader); - SAL_INFO("drawinglayer", "EMF+\t\t\t\t\tPoints: " << std::dec << pathPoints); - SAL_INFO("drawinglayer", "EMF+\t\t\t\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPath (brush path gradient)"); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tHeader: 0x" << std::hex << pathHeader); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tPoints: " << std::dec << pathPoints); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec); path.reset( new EMFPPath(pathPoints) ); path->Read(s, pathFlags); @@ -158,7 +153,7 @@ namespace emfplushelper const ::basegfx::B2DRectangle aBounds(::basegfx::utils::getRange(path->GetPolygon(rR, false))); aWidth = aBounds.getWidth(); aHeight = aBounds.getHeight(); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " << aBounds.getWidth() << "x" << aBounds.getHeight()); } else @@ -167,7 +162,7 @@ namespace emfplushelper s.ReadInt32(boundaryPointCount); sal_uInt64 const pos = s.Tell(); - SAL_INFO("drawinglayer", "EMF+\t use boundary, points: " << boundaryPointCount); + SAL_INFO("drawinglayer.emf", "EMF+\t use boundary, points: " << boundaryPointCount); path.reset( new EMFPPath(boundaryPointCount) ); path->Read(s, 0x0); @@ -176,68 +171,61 @@ namespace emfplushelper const ::basegfx::B2DRectangle aBounds(::basegfx::utils::getRange(path->GetPolygon(rR, false))); aWidth = aBounds.getWidth(); aHeight = aBounds.getHeight(); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPolygon bounding box: " << aBounds.getMinX() << "," << aBounds.getMinY() << " " << aBounds.getWidth() << "x" << aBounds.getHeight()); } - if (additionalFlags & 0x02) + if (additionalFlags & 0x02) // BrushDataTransform { EmfPlusHelperData::readXForm(s, brush_transformation); hasTransformation = true; - SAL_INFO("drawinglayer", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation); } - if (additionalFlags & 0x08) + // BrushDataPresetColors and BrushDataBlendFactorsH + if ((additionalFlags & 0x04) && (additionalFlags & 0x08)) { - s.ReadInt32(blendPoints); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tuse blend, points: " << blendPoints); - if (blendPoints<0 || o3tl::make_unsigned(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float))) - blendPoints = SAL_MAX_INT32 / (2 * sizeof(float)); + SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH"); + return; + } + if (additionalFlags & 0x08) // BrushDataBlendFactorsH + { + s.ReadUInt32(blendPoints); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tuse blend, points: " << blendPoints); blendPositions.reset( new float[2 * blendPoints] ); blendFactors = blendPositions.get() + blendPoints; - for (int i = 0; i < blendPoints; i++) + for (sal_uInt32 i = 0; i < blendPoints; i++) { s.ReadFloat(blendPositions[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tposition[" << i << "]: " << blendPositions[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tposition[" << i << "]: " << blendPositions[i]); } - for (int i = 0; i < blendPoints; i++) + for (sal_uInt32 i = 0; i < blendPoints; i++) { s.ReadFloat(blendFactors[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]); } } - if (additionalFlags & 0x04) + if (additionalFlags & 0x04) // BrushDataPresetColors { - s.ReadInt32(colorblendPoints); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints); - - if (colorblendPoints<0 || o3tl::make_unsigned(colorblendPoints)>SAL_MAX_INT32 / sizeof(float)) - { - colorblendPoints = SAL_MAX_INT32 / sizeof(float); - } - - if (o3tl::make_unsigned(colorblendPoints) > SAL_MAX_INT32 / sizeof(::Color)) - { - colorblendPoints = SAL_MAX_INT32 / sizeof(::Color); - } - + s.ReadUInt32(colorblendPoints); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints); colorblendPositions.reset( new float[colorblendPoints] ); colorblendColors.reset( new ::Color[colorblendPoints] ); - for (int i = 0; i < colorblendPoints; i++) + for (sal_uInt32 i = 0; i < colorblendPoints; i++) { s.ReadFloat(colorblendPositions[i]); - SAL_INFO("drawinglayer", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]); + SAL_INFO("drawinglayer.emf", "EMF+\tposition[" << i << "]: " << colorblendPositions[i]); } - for (int i = 0; i < colorblendPoints; i++) + for (sal_uInt32 i = 0; i < colorblendPoints; i++) { s.ReadUInt32(color); - colorblendColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec); + colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec); } } @@ -246,80 +234,73 @@ namespace emfplushelper case BrushTypeLinearGradient: { s.ReadUInt32(additionalFlags).ReadInt32(wrapMode); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tLinear gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec << ", wrapMode: " << wrapMode); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tLinear gradient, additional flags: 0x" << std::hex << additionalFlags << std::dec << ", wrapMode: " << wrapMode); s.ReadFloat(firstPointX).ReadFloat(firstPointY).ReadFloat(aWidth).ReadFloat(aHeight); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tFirst gradient point: " << firstPointX << ":" << firstPointY + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFirst gradient point: " << firstPointX << ":" << firstPointY << ", size " << aWidth << "x" << aHeight); sal_uInt32 color; s.ReadUInt32(color); - solidColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tfirst color: 0x" << std::hex << color << std::dec); + solidColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tfirst color: 0x" << std::hex << color << std::dec); s.ReadUInt32(color); - secondColor = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tsecond color: 0x" << std::hex << color << std::dec); + secondColor = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tsecond color: 0x" << std::hex << color << std::dec); // repeated colors, unknown meaning, see http://www.aces.uiuc.edu/~jhtodd/Metafile/MetafileRecords/ObjectBrush.html s.ReadUInt32(color); s.ReadUInt32(color); - if (additionalFlags & 0x02) + if (additionalFlags & 0x02) //BrushDataTransform { EmfPlusHelperData::readXForm(s, brush_transformation); hasTransformation = true; - SAL_INFO("drawinglayer", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse brush transformation: " << brush_transformation); + } + // BrushDataPresetColors and BrushDataBlendFactorsH + if ((additionalFlags & 0x04) && (additionalFlags & 0x08)) + { + SAL_WARN("drawinglayer.emf", "EMF+\t Brush must not contain both BrushDataPresetColors and BrushDataBlendFactorsH"); + return; } - if (additionalFlags & 0x08) + if (additionalFlags & 0x08) // BrushDataBlendFactorsH { - s.ReadInt32(blendPoints); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tUse blend, points: " << blendPoints); - if (blendPoints<0 || o3tl::make_unsigned(blendPoints)>SAL_MAX_INT32 / (2 * sizeof(float))) - blendPoints = SAL_MAX_INT32 / (2 * sizeof(float)); + s.ReadUInt32(blendPoints); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse blend, points: " << blendPoints); blendPositions.reset( new float[2 * blendPoints] ); blendFactors = blendPositions.get() + blendPoints; - for (int i = 0; i < blendPoints; i++) + for (sal_uInt32 i = 0; i < blendPoints; i++) { s.ReadFloat(blendPositions[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPosition[" << i << "]: " << blendPositions[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << blendPositions[i]); } - for (int i = 0; i < blendPoints; i++) + for (sal_uInt32 i = 0; i < blendPoints; i++) { s.ReadFloat(blendFactors[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tFactor[" << i << "]: " << blendFactors[i]); } } if (additionalFlags & 0x04) { - s.ReadInt32(colorblendPoints); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints); - - if (colorblendPoints<0 || o3tl::make_unsigned(colorblendPoints)>SAL_MAX_INT32 / sizeof(float)) - { - colorblendPoints = SAL_MAX_INT32 / sizeof(float); - } - - if (o3tl::make_unsigned(colorblendPoints) > SAL_MAX_INT32 / sizeof(::Color)) - { - colorblendPoints = sal_uInt32(SAL_MAX_INT32) / sizeof(::Color); - } - + s.ReadUInt32(colorblendPoints); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tUse color blend, points: " << colorblendPoints); colorblendPositions.reset( new float[colorblendPoints] ); colorblendColors.reset( new ::Color[colorblendPoints] ); - for (int i = 0; i < colorblendPoints; i++) + for (sal_uInt32 i = 0; i < colorblendPoints; i++) { s.ReadFloat(colorblendPositions[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tPosition[" << i << "]: " << colorblendPositions[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tPosition[" << i << "]: " << colorblendPositions[i]); } - for (int i = 0; i < colorblendPoints; i++) + for (sal_uInt32 i = 0; i < colorblendPoints; i++) { s.ReadUInt32(color); - colorblendColors[i] = ::Color(0xff - (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec); + colorblendColors[i] = ::Color(ColorAlpha, (color >> 24), (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tColor[" << i << "]: 0x" << std::hex << color << std::dec); } } @@ -327,7 +308,7 @@ namespace emfplushelper } default: { - SAL_WARN("drawinglayer", "EMF+\tunhandled brush type: " << std::hex << type << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+\tunhandled brush type: " << std::hex << type << std::dec); } } } diff --git a/drawinglayer/source/tools/emfpbrush.hxx b/drawinglayer/source/tools/emfpbrush.hxx index 49706c615898..aee3fe02f60e 100644 --- a/drawinglayer/source/tools/emfpbrush.hxx +++ b/drawinglayer/source/tools/emfpbrush.hxx @@ -17,8 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPBRUSH_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPBRUSH_HXX +#pragma once #include "emfphelperdata.hxx" #include <tools/color.hxx> @@ -91,7 +90,7 @@ namespace emfplushelper BrushTypeLinearGradient = 0x00000004 }; - struct EMFPPath; + class EMFPPath; struct EMFPBrush : public EMFPObject { @@ -105,13 +104,13 @@ namespace emfplushelper ::Color secondColor; // first color is stored in solidColor; basegfx::B2DHomMatrix brush_transformation; bool hasTransformation; - sal_Int32 blendPoints; + sal_uInt32 blendPoints; std::unique_ptr<float[]> blendPositions; float* blendFactors; - sal_Int32 colorblendPoints; + sal_uInt32 colorblendPoints; std::unique_ptr<float[]> colorblendPositions; std::unique_ptr<::Color[]> colorblendColors; - sal_Int32 surroundColorsNumber; + sal_uInt32 surroundColorsNumber; std::unique_ptr<::Color[]> surroundColors; std::unique_ptr<EMFPPath> path; EmfPlusHatchStyle hatchStyle; @@ -126,6 +125,4 @@ namespace emfplushelper }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpcustomlinecap.cxx b/drawinglayer/source/tools/emfpcustomlinecap.cxx index 98af0f5241c7..e457a36cc2c4 100644 --- a/drawinglayer/source/tools/emfpcustomlinecap.cxx +++ b/drawinglayer/source/tools/emfpcustomlinecap.cxx @@ -21,6 +21,7 @@ #include "emfpcustomlinecap.hxx" #include "emfppath.hxx" #include "emfppen.hxx" +#include <basegfx/matrix/b2dhommatrixtools.hxx> using namespace ::com::sun::star; using namespace ::basegfx; @@ -39,33 +40,27 @@ namespace emfplushelper , strokeEndCap(0) , strokeJoin(0) , miterLimit(0.0) + , widthScale(0.0) , mbIsFilled(false) { } - void EMFPCustomLineCap::SetAttributes(rendering::StrokeAttributes& aAttributes) - { - aAttributes.StartCapType = EMFPPen::lcl_convertStrokeCap(strokeStartCap); - aAttributes.EndCapType = EMFPPen::lcl_convertStrokeCap(strokeEndCap); - aAttributes.JoinType = EMFPPen::lcl_convertLineJoinType(strokeJoin); - - aAttributes.MiterLimit = miterLimit; - } - void EMFPCustomLineCap::ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill) { sal_Int32 pathLength; s.ReadInt32(pathLength); - SAL_INFO("drawinglayer", "EMF+\t\tpath length: " << pathLength); + SAL_INFO("drawinglayer.emf", "EMF+\t\tpath length: " << pathLength); sal_uInt32 pathHeader; sal_Int32 pathPoints, pathFlags; s.ReadUInt32(pathHeader).ReadInt32(pathPoints).ReadInt32(pathFlags); - SAL_INFO("drawinglayer", "EMF+\t\tpath (custom cap line path)"); - SAL_INFO("drawinglayer", "EMF+\t\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\tpath (custom cap line path)"); + SAL_INFO("drawinglayer.emf", "EMF+\t\theader: 0x" << std::hex << pathHeader << " points: " << std::dec << pathPoints << " additional flags: 0x" << std::hex << pathFlags << std::dec); EMFPPath path(pathPoints); path.Read(s, pathFlags); polygon = path.GetPolygon(rR, false); + // rotate polygon by 180 degrees + polygon.transform(basegfx::utils::createRotateB2DHomMatrix(M_PI)); mbIsFilled = bFill; } @@ -73,14 +68,13 @@ namespace emfplushelper { sal_uInt32 header; s.ReadUInt32(header).ReadUInt32(type); - SAL_INFO("drawinglayer", "EMF+\t\tcustom cap"); - SAL_INFO("drawinglayer", "EMF+\t\theader: 0x" << std::hex << header << " type: " << type << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\tcustom cap"); + SAL_INFO("drawinglayer.emf", "EMF+\t\theader: 0x" << std::hex << header << " type: " << type << std::dec); if (type == EmfPlusCustomLineCapDataTypeDefault) { sal_uInt32 customLineCapDataFlags, baseCap; float baseInset; - float widthScale; float fillHotSpotX, fillHotSpotY, strokeHotSpotX, strokeHotSpotY; s.ReadUInt32(customLineCapDataFlags).ReadUInt32(baseCap).ReadFloat(baseInset) @@ -88,14 +82,9 @@ namespace emfplushelper .ReadFloat(miterLimit).ReadFloat(widthScale) .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(strokeHotSpotX).ReadFloat(strokeHotSpotY); - SAL_INFO("drawinglayer", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags); - SAL_INFO("drawinglayer", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap); - SAL_INFO("drawinglayer", "EMF+\t\tbaseInset: " << baseInset); - SAL_INFO("drawinglayer", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap); - SAL_INFO("drawinglayer", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap); - SAL_INFO("drawinglayer", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin); - SAL_INFO("drawinglayer", "EMF+\t\tmiterLimit: " << miterLimit); - SAL_INFO("drawinglayer", "EMF+\t\twidthScale: " << widthScale); + SAL_INFO("drawinglayer.emf", "EMF+\t\tcustomLineCapDataFlags: 0x" << std::hex << customLineCapDataFlags); + SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseCap: 0x" << std::hex << baseCap); + SAL_INFO("drawinglayer.emf", "EMF+\t\tbaseInset: " << baseInset); if (customLineCapDataFlags & EmfPlusCustomLineCapDataFillPath) { @@ -112,16 +101,20 @@ namespace emfplushelper // TODO only reads the data, does not use them [I've had // no test document to be able to implement it] - sal_Int32 width, height, middleInset, fillState, lineStartCap; - sal_Int32 lineEndCap, lineJoin, widthScale; - float fillHotSpotX, fillHotSpotY, lineHotSpotX, lineHotSpotY; + sal_Int32 fillState; + float width, height, middleInset, unusedHotSpot; - s.ReadInt32(width).ReadInt32(height).ReadInt32(middleInset).ReadInt32(fillState).ReadInt32(lineStartCap) - .ReadInt32(lineEndCap).ReadInt32(lineJoin).ReadFloat(miterLimit).ReadInt32(widthScale) - .ReadFloat(fillHotSpotX).ReadFloat(fillHotSpotY).ReadFloat(lineHotSpotX).ReadFloat(lineHotSpotY); + s.ReadFloat(width).ReadFloat(height).ReadFloat(middleInset).ReadInt32(fillState).ReadUInt32(strokeStartCap) + .ReadUInt32(strokeEndCap).ReadUInt32(strokeJoin).ReadFloat(miterLimit).ReadFloat(widthScale) + .ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot).ReadFloat(unusedHotSpot); - SAL_INFO("drawinglayer", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tTODO - actually read EmfPlusCustomLineCapArrowData object (section 2.2.2.12)"); } + SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeStartCap: 0x" << std::hex << strokeStartCap); + SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeEndCap: 0x" << std::hex << strokeEndCap); + SAL_INFO("drawinglayer.emf", "EMF+\t\tstrokeJoin: 0x" << std::hex << strokeJoin); + SAL_INFO("drawinglayer.emf", "EMF+\t\tmiterLimit: " << miterLimit); + SAL_INFO("drawinglayer.emf", "EMF+\t\twidthScale: " << widthScale); } } diff --git a/drawinglayer/source/tools/emfpcustomlinecap.hxx b/drawinglayer/source/tools/emfpcustomlinecap.hxx index 3fd2d2fc64a8..22ed6be6dd4d 100644 --- a/drawinglayer/source/tools/emfpcustomlinecap.hxx +++ b/drawinglayer/source/tools/emfpcustomlinecap.hxx @@ -17,10 +17,8 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPCUSTOMLINECAP_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPCUSTOMLINECAP_HXX +#pragma once -#include <com/sun/star/rendering/StrokeAttributes.hpp> #include "emfphelperdata.hxx" namespace emfplushelper @@ -29,18 +27,15 @@ namespace emfplushelper { sal_uInt32 type; sal_uInt32 strokeStartCap, strokeEndCap, strokeJoin; - float miterLimit; + float miterLimit, widthScale; basegfx::B2DPolyPolygon polygon; bool mbIsFilled; EMFPCustomLineCap(); - void SetAttributes(com::sun::star::rendering::StrokeAttributes& aAttributes); void ReadPath(SvStream& s, EmfPlusHelperData const & rR, bool bFill); void Read(SvStream& s, EmfPlusHelperData const & rR); }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpfont.cxx b/drawinglayer/source/tools/emfpfont.cxx index f641fdd4d21b..3fd6537e75a0 100644 --- a/drawinglayer/source/tools/emfpfont.cxx +++ b/drawinglayer/source/tools/emfpfont.cxx @@ -18,27 +18,28 @@ */ #include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> #include "emfpfont.hxx" namespace emfplushelper { static OUString FontStyleToString(sal_uInt32 style) { - OUString sStyle; + OUStringBuffer sStyle; if (style & FontStyleBold) sStyle = "\n\t\t\tFontStyleBold"; if (style & FontStyleItalic) - sStyle = sStyle.concat("\n\t\t\tFontStyleItalic"); + sStyle.append("\n\t\t\tFontStyleItalic"); if (style & FontStyleUnderline) - sStyle = sStyle.concat("\n\t\t\tFontStyleUnderline"); + sStyle.append("\n\t\t\tFontStyleUnderline"); if (style & FontStyleStrikeout) - sStyle = sStyle.concat("\n\t\t\tFontStyleStrikeout"); + sStyle.append("\n\t\t\tFontStyleStrikeout"); - return sStyle; + return sStyle.makeStringAndClear(); } void EMFPFont::Read(SvMemoryStream &s) @@ -47,14 +48,14 @@ namespace emfplushelper sal_uInt32 reserved; sal_uInt32 length; s.ReadUInt32(header).ReadFloat(emSize).ReadUInt32(sizeUnit).ReadInt32(fontFlags).ReadUInt32(reserved).ReadUInt32(length); - SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer", "Invalid header - not 0xdbc01"); - SAL_INFO("drawinglayer", "EMF+\tHeader: 0x" << std::hex << (header >> 12)); - SAL_INFO("drawinglayer", "EMF+\tVersion: 0x" << (header & 0x1fff)); - SAL_INFO("drawinglayer", "EMF+\tSize: " << std::dec << emSize); - SAL_INFO("drawinglayer", "EMF+\tUnit: " << UnitTypeToString(sizeUnit) << " (0x" << std::hex << sizeUnit << ")" << std::dec); - SAL_INFO("drawinglayer", "EMF+\tFlags: " << FontStyleToString(fontFlags) << " (0x" << std::hex << fontFlags << ")"); - SAL_INFO("drawinglayer", "EMF+\tReserved: 0x" << reserved << std::dec); - SAL_INFO("drawinglayer", "EMF+\tLength: " << length); + SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer.emf", "Invalid header - not 0xdbc01"); + SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << (header >> 12)); + SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << (header & 0x1fff)); + SAL_INFO("drawinglayer.emf", "EMF+\tSize: " << std::dec << emSize); + SAL_INFO("drawinglayer.emf", "EMF+\tUnit: " << UnitTypeToString(sizeUnit) << " (0x" << std::hex << sizeUnit << ")" << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << FontStyleToString(fontFlags) << " (0x" << std::hex << fontFlags << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\tReserved: 0x" << reserved << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tLength: " << length); if (length <= 0 || length >= 0x4000) return; @@ -68,7 +69,7 @@ namespace emfplushelper } family = OUString(pStr, SAL_NO_ACQUIRE); - SAL_INFO("drawinglayer", "EMF+\tFamily: " << family); + SAL_INFO("drawinglayer.emf", "EMF+\tFamily: " << family); } } diff --git a/drawinglayer/source/tools/emfpfont.hxx b/drawinglayer/source/tools/emfpfont.hxx index 0559cb949487..3f9e24166850 100644 --- a/drawinglayer/source/tools/emfpfont.hxx +++ b/drawinglayer/source/tools/emfpfont.hxx @@ -17,8 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPFONT_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPFONT_HXX +#pragma once #include "emfphelperdata.hxx" @@ -46,6 +45,4 @@ namespace emfplushelper }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfphelperdata.cxx b/drawinglayer/source/tools/emfphelperdata.cxx index cc0770c9a8fb..9f583702dcc2 100644 --- a/drawinglayer/source/tools/emfphelperdata.cxx +++ b/drawinglayer/source/tools/emfphelperdata.cxx @@ -29,16 +29,14 @@ #include "emfpstringformat.hxx" #include <basegfx/curve/b2dcubicbezier.hxx> #include <wmfemfhelper.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> @@ -51,7 +49,6 @@ #include <basegfx/polygon/b2dpolygontools.hxx> #include <basegfx/polygon/b2dpolypolygoncutter.hxx> #include <sal/log.hxx> -#include <vcl/outdev.hxx> #include <vcl/svapp.hxx> #include <vcl/settings.hxx> #include <i18nlangtag/languagetag.hxx> @@ -84,6 +81,9 @@ namespace emfplushelper case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects"; case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon"; case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines"; + case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve"; + case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve"; + case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve"; case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse"; case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse"; case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie"; @@ -128,89 +128,89 @@ namespace emfplushelper { switch (type) { - case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush"; - case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen"; - case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath"; - case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion"; - case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage"; - case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont"; - case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat"; - case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes"; - case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap"; + case EmfPlusObjectTypeBrush: return u"EmfPlusObjectTypeBrush"_ustr; + case EmfPlusObjectTypePen: return u"EmfPlusObjectTypePen"_ustr; + case EmfPlusObjectTypePath: return u"EmfPlusObjectTypePath"_ustr; + case EmfPlusObjectTypeRegion: return u"EmfPlusObjectTypeRegion"_ustr; + case EmfPlusObjectTypeImage: return u"EmfPlusObjectTypeImage"_ustr; + case EmfPlusObjectTypeFont: return u"EmfPlusObjectTypeFont"_ustr; + case EmfPlusObjectTypeStringFormat: return u"EmfPlusObjectTypeStringFormat"_ustr; + case EmfPlusObjectTypeImageAttributes: return u"EmfPlusObjectTypeImageAttributes"_ustr; + case EmfPlusObjectTypeCustomLineCap: return u"EmfPlusObjectTypeCustomLineCap"_ustr; } - return ""; + return u""_ustr; } static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset) { switch (nPixelOffset) { - case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault"; - case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed"; - case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality"; - case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone"; - case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf"; + case PixelOffsetMode::PixelOffsetModeDefault: return u"PixelOffsetModeDefault"_ustr; + case PixelOffsetMode::PixelOffsetModeHighSpeed: return u"PixelOffsetModeHighSpeed"_ustr; + case PixelOffsetMode::PixelOffsetModeHighQuality: return u"PixelOffsetModeHighQuality"_ustr; + case PixelOffsetMode::PixelOffsetModeNone: return u"PixelOffsetModeNone"_ustr; + case PixelOffsetMode::PixelOffsetModeHalf: return u"PixelOffsetModeHalf"_ustr; } - return ""; + return u""_ustr; } static OUString SmoothingModeToString(sal_uInt16 nSmoothMode) { switch (nSmoothMode) { - case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault"; - case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed"; - case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality"; - case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone"; - case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4"; - case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8"; + case SmoothingMode::SmoothingModeDefault: return u"SmoothingModeDefault"_ustr; + case SmoothingMode::SmoothingModeHighSpeed: return u"SmoothModeHighSpeed"_ustr; + case SmoothingMode::SmoothingModeHighQuality: return u"SmoothingModeHighQuality"_ustr; + case SmoothingMode::SmoothingModeNone: return u"SmoothingModeNone"_ustr; + case SmoothingMode::SmoothingModeAntiAlias8x4: return u"SmoothingModeAntiAlias8x4"_ustr; + case SmoothingMode::SmoothingModeAntiAlias8x8: return u"SmoothingModeAntiAlias8x8"_ustr; } - return ""; + return u""_ustr; } static OUString TextRenderingHintToString(sal_uInt16 nHint) { switch (nHint) { - case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault"; - case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit"; - case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel"; - case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit"; - case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias"; - case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit"; + case TextRenderingHint::TextRenderingHintSystemDefault: return u"TextRenderingHintSystemDefault"_ustr; + case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return u"TextRenderingHintSingleBitPerPixelGridFit"_ustr; + case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return u"TextRenderingHintSingleBitPerPixel"_ustr; + case TextRenderingHint::TextRenderingHintAntialiasGridFit: return u"TextRenderingHintAntialiasGridFit"_ustr; + case TextRenderingHint::TextRenderingHintAntialias: return u"TextRenderingHintAntialias"_ustr; + case TextRenderingHint::TextRenderingHintClearTypeGridFit: return u"TextRenderingHintClearTypeGridFit"_ustr; } - return ""; + return u""_ustr; } static OUString InterpolationModeToString(sal_uInt16 nMode) { switch (nMode) { - case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault"; - case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality"; - case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality"; - case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear"; - case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic"; - case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor"; - case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear"; - case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic"; + case InterpolationMode::InterpolationModeDefault: return u"InterpolationModeDefault"_ustr; + case InterpolationMode::InterpolationModeLowQuality: return u"InterpolationModeLowQuality"_ustr; + case InterpolationMode::InterpolationModeHighQuality: return u"InterpolationModeHighQuality"_ustr; + case InterpolationMode::InterpolationModeBilinear: return u"InterpolationModeBilinear"_ustr; + case InterpolationMode::InterpolationModeBicubic: return u"InterpolationModeBicubic"_ustr; + case InterpolationMode::InterpolationModeNearestNeighbor: return u"InterpolationModeNearestNeighbor"_ustr; + case InterpolationMode::InterpolationModeHighQualityBilinear: return u"InterpolationModeHighQualityBilinear"_ustr; + case InterpolationMode::InterpolationModeHighQualityBicubic: return u"InterpolationModeHighQualityBicubic"_ustr; } - return ""; + return u""_ustr; } OUString UnitTypeToString(sal_uInt16 nType) { switch (nType) { - case UnitTypeWorld: return "UnitTypeWorld"; - case UnitTypeDisplay: return "UnitTypeDisplay"; - case UnitTypePixel: return "UnitTypePixel"; - case UnitTypePoint: return "UnitTypePoint"; - case UnitTypeInch: return "UnitTypeInch"; - case UnitTypeDocument: return "UnitTypeDocument"; - case UnitTypeMillimeter: return "UnitTypeMillimeter"; + case UnitTypeWorld: return u"UnitTypeWorld"_ustr; + case UnitTypeDisplay: return u"UnitTypeDisplay"_ustr; + case UnitTypePixel: return u"UnitTypePixel"_ustr; + case UnitTypePoint: return u"UnitTypePoint"_ustr; + case UnitTypeInch: return u"UnitTypeInch"_ustr; + case UnitTypeDocument: return u"UnitTypeDocument"_ustr; + case UnitTypeMillimeter: return u"UnitTypeMillimeter"_ustr; } - return ""; + return u""_ustr; } static bool IsBrush(sal_uInt16 flags) @@ -220,47 +220,43 @@ namespace emfplushelper static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid) { - OUString sBrushId; - if (IsBrush(flags)) - sBrushId = sBrushId.concat("EmfPlusBrush ID: ").concat(OUString::number(brushid)); + return "EmfPlusBrush ID: " + OUString::number(brushid); else - sBrushId = sBrushId.concat("ARGB: 0x").concat(OUString::number(brushid, 16)); - - return sBrushId; + return "ARGB: 0x" + OUString::number(brushid, 16); } EMFPObject::~EMFPObject() { } - float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI) + double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d) { - switch (aUnitType) + switch (static_cast<UnitType>(aUnitType)) { case UnitTypePixel: - return 1.0f; + return n; case UnitTypePoint: - return aDPI / 72.0; + return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d); case UnitTypeInch: - return aDPI; + return n * DPI(d); case UnitTypeMillimeter: - return aDPI / 25.4; + return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d); case UnitTypeDocument: - return aDPI / 300.0; + return n * DPI(d) / 300.0; case UnitTypeWorld: case UnitTypeDisplay: - SAL_WARN("drawinglayer", "EMF+\t Converting to World/Display."); - return 1.0f; + SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display."); + return n; default: - SAL_WARN("drawinglayer", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); - return 1.0f; + SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); + return n; } } @@ -268,9 +264,9 @@ namespace emfplushelper { sal_uInt16 objecttype = flags & 0x7f00; sal_uInt16 index = flags & 0xff; - SAL_INFO("drawinglayer", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")"); - SAL_INFO("drawinglayer", "EMF+\tObject slot: " << index); - SAL_INFO("drawinglayer", "EMF+\tFlags: " << (flags & 0xff00)); + SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index); + SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00)); switch (objecttype) { @@ -286,21 +282,20 @@ namespace emfplushelper EMFPPen *pen = new EMFPPen(); maEMFPObjects[index].reset(pen); pen->Read(rObjectStream, *this); - pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI); + pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal); break; } case EmfPlusObjectTypePath: { - sal_uInt32 header, pathFlags; - sal_Int32 points; - - rObjectStream.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags); - SAL_INFO("drawinglayer", "EMF+\t\tHeader: 0x" << std::hex << header); - SAL_INFO("drawinglayer", "EMF+\t\tPoints: " << std::dec << points); - SAL_INFO("drawinglayer", "EMF+\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec); - EMFPPath *path = new EMFPPath(points); + sal_uInt32 aVersion, aPathPointCount, aPathPointFlags; + + rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags); + SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion); + SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount); + SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec); + EMFPPath *path = new EMFPPath(aPathPointCount); maEMFPObjects[index].reset(path); - path->Read(rObjectStream, pathFlags); + path->Read(rObjectStream, aPathPointFlags); break; } case EmfPlusObjectTypeRegion: @@ -331,7 +326,7 @@ namespace emfplushelper font->fontFlags = 0; font->Read(rObjectStream); // tdf#113624 Convert unit to Pixels - font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI); + font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal); break; } @@ -351,12 +346,12 @@ namespace emfplushelper } case EmfPlusObjectTypeCustomLineCap: { - SAL_WARN("drawinglayer", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); break; } default: { - SAL_WARN("drawinglayer", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); } } } @@ -368,7 +363,7 @@ namespace emfplushelper // specifies a location in the coordinate space that is relative to // the location specified by the previous element in the array. In the case of the first element in // PointData, a previous location at coordinates (0,0) is assumed. - SAL_WARN("drawinglayer", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); } if (flags & 0x4000) @@ -435,7 +430,7 @@ namespace emfplushelper { if (mnPixX == 0 || mnPixY == 0) { - SAL_WARN("drawinglayer", "dimensions in pixels is 0"); + SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0"); return; } // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes. @@ -452,6 +447,10 @@ namespace emfplushelper maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY, double(-mnFrameLeft), double(-mnFrameTop)); maMapTransform *= maBaseTransform; + + // Used only for performance optimization, to do not calculate it every line draw + mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b()); + mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d()); } ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const @@ -464,15 +463,17 @@ namespace emfplushelper Color color; if (flags & 0x8000) // we use a color { - color = Color(0xff - (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, + color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); } - else // we use a pen + else // we use a brush { - const EMFPPen* pen = static_cast<EMFPPen*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); - if (pen) + const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); + if (brush) { - color = pen->GetColor(); + color = brush->GetColor(); + if (brush->type != BrushTypeSolidColor) + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported"); } } return color; @@ -485,212 +486,184 @@ namespace emfplushelper if ( iter != map.end() ) { map.erase( iter ); - SAL_INFO("drawinglayer", "EMF+\t\tStack index: " << index << " found and erased"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased"); } wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current(); // tdf#112500 We need to save world transform somehow, during graphic state push state.setTransformation(maWorldTransform); - map[ index ] = state; + map[ index ] = std::move(state); } - void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState) + void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index) { - GraphicStateMap::iterator iter = map.find( index ); + GraphicStateMap::iterator iter = map.find(index); - if ( iter != map.end() ) + if (iter != map.end()) { wmfemfhelper::PropertyHolder state = iter->second; maWorldTransform = state.getTransformation(); - rState.setClipPolyPolygon( state.getClipPolyPolygon() ); + if (state.getClipPolyPolygonActive()) + { + SAL_INFO("drawinglayer.emf", + "EMF+\t Restore clipping region to saved in index: " << index); + wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders, + mrPropertyHolders); + } + else + { + SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping"); + wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, + mrPropertyHolders); + } mappingChanged(); - SAL_INFO("drawinglayer", "EMF+\t\tStack index: " << index << " found, maWorldTransform: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", + "EMF+\t\tStack index: " << index + << " found, maWorldTransform: " << maWorldTransform); } } - void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex) + drawinglayer::attribute::LineStartEndAttribute + EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const { - const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get()); - SAL_WARN_IF(!pen, "drawinglayer", "emf+ missing pen"); - - if (!(pen && polygon.count())) - return; - - // we need a line join attribute - basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round; - if (pen->penDataFlags & EmfPlusPenDataJoin) // additional line join information + const double pw = mdExtractedYScale * aPenWidth; + if (aCap == LineCapTypeSquare) { - lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin)); + basegfx::B2DPolygon aCapPolygon( + { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - - // we need a line cap attribute - css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT; - if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap information + else if (aCap == LineCapTypeRound) { - lineCap = static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap)); - SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer", "emf+ pen uses different start and end cap"); + basegfx::B2DPolygon aCapPolygon( + { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827}, + {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236}, + {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - - const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth; - drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(), - transformedPenWidth, - lineJoin, - lineCap); - - drawinglayer::attribute::StrokeAttribute aStrokeAttribute; - if (pen->penDataFlags & EmfPlusPenDataLineStyle && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style + else if (aCap == LineCapTypeTriangle) { - // short writing - const double pw = maMapTransform.get(1, 1) * pen->penWidth; - // taken from the old cppcanvas implementation and multiplied with pen width - const std::vector<double> dash = { 3*pw, 3*pw }; - const std::vector<double> dot = { pw, 3*pw }; - const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw }; - const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw }; - - switch (pen->dashStyle) - { - case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute - break; - case EmfPlusLineStyleDash: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dash); - break; - case EmfPlusLineStyleDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dot); - break; - case EmfPlusLineStyleDashDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdot); - break; - case EmfPlusLineStyleDashDotDot: - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdotdot); - break; - } + basegfx::B2DPolygon aCapPolygon( + { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - else if (pen->penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line + else if (aCap == LineCapTypeSquareAnchor) { - // StrokeAttribute needs a double vector while the pen provides a float vector - std::vector<double> aPattern(pen->dashPattern.size()); - for (size_t i=0; i<aPattern.size(); i++) - { - // convert from float to double and multiply with the adjusted pen width - aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i]; - } - aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(aPattern); + basegfx::B2DPolygon aCapPolygon( + { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - - if (pen->GetColor().GetTransparency() == 0) + else if (aCap == LineCapTypeRoundAnchor) { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - polygon, - lineAttribute, - aStrokeAttribute)); + const basegfx::B2DPolygon aCapPolygon + = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0); + return drawinglayer::attribute::LineStartEndAttribute( + 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - else + else if (aCap == LineCapTypeDiamondAnchor) { - const drawinglayer::primitive2d::Primitive2DReference aPrimitive( - new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( - polygon, - lineAttribute, - aStrokeAttribute)); - - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( - drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, - pen->GetColor().GetTransparency() / 255.0)); + basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5}, + {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5}, + {-1.0, 0.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); } - - if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && (pen->customStartCap->polygon.begin()->count() > 1)) + else if (aCap == LineCapTypeArrowAnchor) { - SAL_WARN("drawinglayer", "EMF+\tCustom Start Line Cap"); - ::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon); - - // get the gradient of the first line in the polypolygon - double x1 = polygon.begin()->getB2DPoint(0).getX(); - double y1 = polygon.begin()->getB2DPoint(0).getY(); - double x2 = polygon.begin()->getB2DPoint(1).getX(); - double y2 = polygon.begin()->getB2DPoint(1).getY(); - - if ((x2 - x1) != 0) - { - double gradient = (y2 - y1) / (x2 - x1); - - // now we get the angle that we need to rotate the arrow by - double angle = (M_PI / 2) - atan(gradient); + basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); + aCapPolygon.setClosed(true); + return drawinglayer::attribute::LineStartEndAttribute( + 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); + } + return drawinglayer::attribute::LineStartEndAttribute(); + } - // rotate the arrow - startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); - } + void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, + sal_uInt32 penIndex) + { + const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get()); + SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen"); - startCapPolygon.transform(maMapTransform); + if (!(pen && polygon.count())) + return; - basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(), - 0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY()); - startCapPolygon.transform(tran); + const double transformedPenWidth = mdExtractedYScale * pen->penWidth; + drawinglayer::attribute::LineAttribute lineAttribute( + pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin, + css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here + pen->fMiterMinimumAngle); - if (pen->customStartCap->mbIsFilled) - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - startCapPolygon, - pen->GetColor().getBColor())); - } + drawinglayer::attribute::LineStartEndAttribute aStart; + if (pen->penDataFlags & EmfPlusPenDataStartCap) + { + if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) + && (pen->customStartCap->polygon.begin()->count() > 1)) + aStart = drawinglayer::attribute::LineStartEndAttribute( + pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale + * pen->customStartCap->widthScale * pen->penWidth, + pen->customStartCap->polygon, false); else - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - startCapPolygon, - lineAttribute, - aStrokeAttribute)); - } + aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth); } - if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) && (pen->customEndCap->polygon.begin()->count() > 1)) + drawinglayer::attribute::LineStartEndAttribute aEnd; + if (pen->penDataFlags & EmfPlusPenDataEndCap) { - SAL_WARN("drawinglayer", "EMF+\tCustom End Line Cap"); - - ::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon); - - // get the gradient of the first line in the polypolygon - double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(); - double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY(); - double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX(); - double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY(); + if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) + && (pen->customEndCap->polygon.begin()->count() > 1)) + aEnd = drawinglayer::attribute::LineStartEndAttribute( + pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale + * pen->customEndCap->widthScale * pen->penWidth, + pen->customEndCap->polygon, false); + else + aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth); + } - if ((x2 - x1) != 0) + if (pen->GetColor().IsTransparent()) + { + drawinglayer::primitive2d::Primitive2DContainer aContainer; + if (aStart.isDefault() && aEnd.isDefault()) + aContainer.append( + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( + polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); + else { - double gradient = (y2 - y1) / (x2 - x1); - - // now we get the angle that we need to rotate the arrow by - double angle = (M_PI / 2) - atan(gradient); - - // rotate the arrow - endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle)); + aContainer.resize(polygon.count()); + for (sal_uInt32 i = 0; i < polygon.count(); i++) + aContainer[i] = + new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D( + polygon.getB2DPolygon(i), lineAttribute, + pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd); } - - endCapPolygon.transform(maMapTransform); - basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(), - 0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY()); - endCapPolygon.transform(tran); - - if (pen->customEndCap->mbIsFilled) - { + mrTargetHolders.Current().append( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0)); + } + else + { + if (aStart.isDefault() && aEnd.isDefault()) mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - endCapPolygon, - pen->GetColor().getBColor())); - } + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( + polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); else - { - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D>( - endCapPolygon, - lineAttribute, - aStrokeAttribute)); - } + for (sal_uInt32 i = 0; i < polygon.count(); i++) + { + mrTargetHolders.Current().append( + new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D( + polygon.getB2DPolygon(i), lineAttribute, + pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd)); + } } - mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor()); mrPropertyHolders.Current().setLineColorActive(true); mrPropertyHolders.Current().setFillColorActive(false); @@ -698,28 +671,25 @@ namespace emfplushelper void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color) { - if (color.GetTransparency() >= 255) + if (color.GetAlpha() == 0) return; - if (color.GetTransparency() == 0) + if (!color.IsTransparent()) { // not transparent mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( polygon, color.getBColor())); } else { - const drawinglayer::primitive2d::Primitive2DReference aPrimitive( - new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( - polygon, - color.getBColor())); - + // transparent mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( - drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }, - color.GetTransparency() / 255.0)); + new drawinglayer::primitive2d::PolyPolygonRGBAPrimitive2D( + polygon, + color.getBColor(), + (255 - color.GetAlpha()) / 255.0)); } } @@ -730,11 +700,11 @@ namespace emfplushelper if (isColor) // use Color { - SAL_INFO("drawinglayer", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec); // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background, // ranging from 0 for completely transparent to 0xFF for completely opaque. - const Color color(0xff - (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); + const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); EMFPPlusFillPolygonSolidColor(polygon, color); mrPropertyHolders.Current().setFillColor(color.getBColor()); @@ -743,8 +713,8 @@ namespace emfplushelper } else // use Brush { - EMFPBrush* brush = static_cast<EMFPBrush*>( maEMFPObjects[brushIndexOrColor & 0xff].get() ); - SAL_INFO("drawinglayer", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")"); + EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); + SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")"); // give up in case something wrong happened if( !brush ) @@ -797,20 +767,20 @@ namespace emfplushelper // temporal solution: create a solid colored polygon // TODO create a 'real' hatching primitive mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( polygon, fillColor.getBColor())); } else if (brush->type == BrushTypeTextureFill) { - SAL_WARN("drawinglayer", "EMF+\tTODO: implement BrushTypeTextureFill brush"); + SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush"); } else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient) { if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1)) { - SAL_WARN("drawinglayer", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: "); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: "); } ::basegfx::B2DHomMatrix aTextureTransformation; @@ -831,63 +801,36 @@ namespace emfplushelper if (brush->blendPositions) { - SAL_INFO("drawinglayer", "EMF+\t\tUse blend"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend"); // store the blendpoints in the vector - for (int i = 0; i < brush->blendPoints; i++) + for (sal_uInt32 i = 0; i < brush->blendPoints; i++) { - double aBlendPoint; + const double aBlendPoint = brush->blendPositions[i]; basegfx::BColor aColor; - if (brush->type == BrushTypeLinearGradient) - { - aBlendPoint = brush->blendPositions [i]; - } - else - { - // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius - aBlendPoint = 2. * ( 1. - brush->blendPositions [i] ); - } - aColor.setGreen( aStartColor.getGreen() + brush->blendFactors[i] * ( aEndColor.getGreen() - aStartColor.getGreen() ) ); - aColor.setBlue ( aStartColor.getBlue() + brush->blendFactors[i] * ( aEndColor.getBlue() - aStartColor.getBlue() ) ); - aColor.setRed ( aStartColor.getRed() + brush->blendFactors[i] * ( aEndColor.getRed() - aStartColor.getRed() ) ); - const double aTransparency = brush->solidColor.GetTransparency() + brush->blendFactors[i] * ( brush->secondColor.GetTransparency() - brush->solidColor.GetTransparency() ); - aVector.emplace_back(aBlendPoint, aColor, (255.0 - aTransparency) / 255.0); + aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen())); + aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue())); + aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed())); + const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha()); + aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0); } } else if (brush->colorblendPositions) { - SAL_INFO("drawinglayer", "EMF+\t\tUse color blend"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend"); // store the colorBlends in the vector - for (int i = 0; i < brush->colorblendPoints; i++) + for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++) { - double aBlendPoint; - basegfx::BColor aColor; - if (brush->type == BrushTypeLinearGradient) - { - aBlendPoint = brush->colorblendPositions [i]; - } - else - { - // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius - aBlendPoint = 2. * ( 1. - brush->colorblendPositions [i] ); - } - aColor = brush->colorblendColors[i].getBColor(); - aVector.emplace_back(aBlendPoint, aColor, (255 - brush->colorblendColors[i].GetTransparency()) / 255.0 ); + const double aBlendPoint = brush->colorblendPositions[i]; + const basegfx::BColor aColor = brush->colorblendColors[i].getBColor(); + aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0); } } else // ok, no extra points: just start and end { - if (brush->type == BrushTypeLinearGradient) - { - aVector.emplace_back(0.0, aStartColor, (255 - brush->solidColor.GetTransparency()) / 255.0); - aVector.emplace_back(1.0, aEndColor, (255 - brush->secondColor.GetTransparency()) / 255.0); - } - else // again, here reverse - { - aVector.emplace_back(0.0, aEndColor, (255 - brush->secondColor.GetTransparency()) / 255.0); - aVector.emplace_back(1.0, aStartColor, (255 - brush->solidColor.GetTransparency()) / 255.0); - } + aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0); + aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0); } // get the polygon range to be able to map the start/end/center point correctly @@ -928,30 +871,30 @@ namespace emfplushelper // create the same one used for SVG mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::SvgLinearGradientPrimitive2D>( + new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D( aTextureTransformation, polygon, - aVector, + std::move(aVector), aStartPoint, aEndPoint, false, // do not use UnitCoordinates aSpreadMethod)); } else // BrushTypePathGradient - { + { // TODO The PathGradient is not implemented, and Radial Gradient is used instead basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY); aCenterPoint = aPolygonTransformation * aCenterPoint; // create the same one used for SVG mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::SvgRadialGradientPrimitive2D>( + new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D( aTextureTransformation, polygon, - aVector, + std::move(aVector), aCenterPoint, - 0.5, // relative radius - true, // use UnitCoordinates to stretch the gradient - drawinglayer::primitive2d::SpreadMethod::Repeat, + 0.7, // relative radius little bigger to cover all elements + true, // use UnitCoordinates to stretch the gradient + drawinglayer::primitive2d::SpreadMethod::Pad, nullptr)); } } @@ -962,11 +905,7 @@ namespace emfplushelper SvMemoryStream& rMS, wmfemfhelper::TargetHolders& rTargetHolders, wmfemfhelper::PropertyHolders& rPropertyHolders) - : maBaseTransform(), - maWorldTransform(), - maMapTransform(), - maEMFPObjects(), - mfPageScale(0.0), + : mfPageScale(0.0), mnOriginX(0), mnOriginY(0), mnHDPI(0), @@ -983,17 +922,18 @@ namespace emfplushelper mnMmY(0), mbMultipart(false), mMFlags(0), - mMStream(), + mdExtractedXScale(1.0), + mdExtractedYScale(1.0), mrTargetHolders(rTargetHolders), mrPropertyHolders(rPropertyHolders), bIsGetDCProcessing(false) { rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom); - SAL_INFO("drawinglayer", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom); + SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom); rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY); - SAL_INFO("drawinglayer", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY); + SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY); readXForm(rMS, maBaseTransform); - SAL_INFO("drawinglayer", "EMF+ base transform: " << maBaseTransform); + SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform); mappingChanged(); } @@ -1013,14 +953,8 @@ namespace emfplushelper } case EmfPlusCombineModeIntersect: { - if (leftPolygon.count()) - { - aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( - leftPolygon, - rightPolygon, - true, - false); - } + aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( + leftPolygon, rightPolygon, true, false); break; } case EmfPlusCombineModeUnion: @@ -1056,7 +990,7 @@ namespace emfplushelper sal_uInt64 length = rMS.GetSize(); if (length < 12) - SAL_WARN("drawinglayer", "length is less than required header size"); + SAL_WARN("drawinglayer.emf", "length is less than required header size"); // 12 is minimal valid EMF+ record size; remaining bytes are padding while (length >= 12) @@ -1071,30 +1005,40 @@ namespace emfplushelper if (size < 12) { - SAL_WARN("drawinglayer", "Size field is less than 12 bytes"); + SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes"); break; } else if (size > length) { - SAL_WARN("drawinglayer", "Size field is greater than bytes left"); + SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left"); break; } if (dataSize > (size - 12)) { - SAL_WARN("drawinglayer", "DataSize field is greater than Size-12"); + SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12"); break; } - SAL_INFO("drawinglayer", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec); - SAL_INFO("drawinglayer", "EMF+\t record size: " << size); - SAL_INFO("drawinglayer", "EMF+\t flags: 0x" << std::hex << flags << std::dec); - SAL_INFO("drawinglayer", "EMF+\t data size: " << dataSize); + SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size); + SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize); if (bIsGetDCProcessing) { - SAL_INFO("drawinglayer", "EMF+\t reset the current clipping region for the world space to infinity."); - wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders); + if (aGetDCState.getClipPolyPolygonActive()) + { + SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved"); + wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders, + mrPropertyHolders); + } + else + { + SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping"); + wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, + mrPropertyHolders); + } bIsGetDCProcessing = false; } if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000))) @@ -1110,13 +1054,13 @@ namespace emfplushelper // 1st 4 bytes are TotalObjectSize mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4); - SAL_INFO("drawinglayer", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize); + SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize); } else { if (mbMultipart) { - SAL_INFO("drawinglayer", "EMF+ multipart record flags: " << mMFlags); + SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags); mMStream.Seek(0); processObjectRecord(mMStream, mMFlags, 0, true); } @@ -1130,14 +1074,15 @@ namespace emfplushelper { case EmfPlusRecordTypeHeader: { - sal_uInt32 header, version; - - rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); - SAL_INFO("drawinglayer", "EMF+\tHeader: 0x" << std::hex << header); - SAL_INFO("drawinglayer", "EMF+\tVersion: " << std::dec << version); - SAL_INFO("drawinglayer", "EMF+\tHorizontal DPI: " << mnHDPI); - SAL_INFO("drawinglayer", "EMF+\tVertical DPI: " << mnVDPI); - SAL_INFO("drawinglayer", "EMF+\tDual: " << ((flags & 1) ? "true" : "false")); + sal_uInt32 version, emfPlusFlags; + SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false")); + + rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); + SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << std::hex << version); + SAL_INFO("drawinglayer.emf", "EMF+\tEmf+ Flags: 0x" << emfPlusFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer")); + SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI); + SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI); break; } case EmfPlusRecordTypeEndOfFile: @@ -1150,7 +1095,7 @@ namespace emfplushelper unsigned char data; OUString hexdata; - SAL_INFO("drawinglayer", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec); for (sal_uInt32 i=0; i<dataSize; i++) { @@ -1166,14 +1111,15 @@ namespace emfplushelper hexdata += "0x" + padding + OUString::number(data, 16) + " "; } - SAL_INFO("drawinglayer", "EMF+\t" << hexdata); + SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata); #endif break; } case EmfPlusRecordTypeGetDC: { bIsGetDCProcessing = true; - SAL_INFO("drawinglayer", "EMF+\tAlready used in svtools wmf/emf filter parser"); + aGetDCState = mrPropertyHolders.Current(); + SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser"); break; } case EmfPlusRecordTypeObject: @@ -1193,21 +1139,21 @@ namespace emfplushelper if (type == EmfPlusRecordTypeFillPie) { rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor); + SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor); } else if (type == EmfPlusRecordTypeDrawPie) { - SAL_INFO("drawinglayer", "EMF+\t DrawPie"); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie"); } else { - SAL_INFO("drawinglayer", "EMF+\t DrawArc"); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc"); } rMS.ReadFloat(startAngle).ReadFloat(sweepAngle); float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); - SAL_INFO("drawinglayer", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); + SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); startAngle = basegfx::deg2rad(startAngle); sweepAngle = basegfx::deg2rad(sweepAngle); float endAngle = startAngle + sweepAngle; @@ -1228,7 +1174,7 @@ namespace emfplushelper std::swap(endAngle, startAngle); } - SAL_INFO("drawinglayer", "EMF+\t Adjusted angles: start " << + SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " << basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) << " startAngle: " << startAngle << " sweepAngle: " << sweepAngle); const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh); @@ -1254,13 +1200,13 @@ namespace emfplushelper sal_uInt32 index = flags & 0xff; sal_uInt32 brushIndexOrColor; rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+ FillPath slot: " << index); + SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index); EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get()); if (path) EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor); else - SAL_WARN("drawinglayer", "EMF+\tEmfPlusRecordTypeFillPath missing path"); + SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path"); } break; case EmfPlusRecordTypeFillRegion: @@ -1268,9 +1214,13 @@ namespace emfplushelper sal_uInt32 index = flags & 0xff; sal_uInt32 brushIndexOrColor; rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t FillRegion slot: " << index); + SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index); - EMFPPlusFillPolygon(static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get())->regionPolyPolygon, flags & 0x8000, brushIndexOrColor); + EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); + if (region) + EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor); + else + SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region"); } break; case EmfPlusRecordTypeDrawEllipse: @@ -1286,10 +1236,10 @@ namespace emfplushelper rMS.ReadUInt32(brushIndexOrColor); } - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff)); + SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff)); float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); - SAL_INFO("drawinglayer", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); + SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); ::basegfx::B2DPolyPolygon polyPolygon( ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh), 0.5 * dw, 0.5 * dh)); @@ -1305,26 +1255,26 @@ namespace emfplushelper { // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used sal_uInt32 brushIndexOrColor = 999; - sal_Int32 rectangles; + ::basegfx::B2DPolyPolygon polyPolygon; + sal_uInt32 rectangles; + float x, y, width, height; const bool isColor = (flags & 0x8000); ::basegfx::B2DPolygon polygon; if (EmfPlusRecordTypeFillRects == type) { - SAL_INFO("drawinglayer", "EMF+\t FillRects"); + SAL_INFO("drawinglayer.emf", "EMF+\t FillRects"); rMS.ReadUInt32(brushIndexOrColor); - SAL_INFO("drawinglayer", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); } else { - SAL_INFO("drawinglayer", "EMF+\t DrawRects"); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects"); } - rMS.ReadInt32(rectangles); - - for (int i = 0; i < rectangles; i++) + rMS.ReadUInt32(rectangles); + for (sal_uInt32 i = 0; i < rectangles; i++) { - float x, y, width, height; ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000)); polygon.clear(); polygon.append(Map(x, y)); @@ -1333,39 +1283,35 @@ namespace emfplushelper polygon.append(Map(x, y + height)); polygon.setClosed(true); - SAL_INFO("drawinglayer", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height); - - ::basegfx::B2DPolyPolygon polyPolygon(polygon); - if (type == EmfPlusRecordTypeFillRects) - EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor); - else - EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); + SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height); + polyPolygon.append(polygon); } + if (type == EmfPlusRecordTypeFillRects) + EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor); + else + EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); break; } case EmfPlusRecordTypeFillPolygon: { - const sal_uInt8 index = flags & 0xff; - sal_uInt32 brushIndexOrColor; - sal_Int32 points; + sal_uInt32 brushIndexOrColor, points; rMS.ReadUInt32(brushIndexOrColor); - rMS.ReadInt32(points); - SAL_INFO("drawinglayer", "EMF+\t FillPolygon in slot: " << index << " points: " << points); - SAL_INFO("drawinglayer", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec); + rMS.ReadUInt32(points); + SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points); + SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec); EMFPPath path(points, true); path.Read(rMS, flags); EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor); - break; } case EmfPlusRecordTypeDrawLines: { sal_uInt32 points; rMS.ReadUInt32(points); - SAL_INFO("drawinglayer", "EMF+\t DrawLines in slot: " << (flags & 0xff) << " points: " << points); + SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points); EMFPPath path(points, true); path.Read(rMS, flags); @@ -1379,13 +1325,13 @@ namespace emfplushelper { sal_uInt32 penIndex; rMS.ReadUInt32(penIndex); - SAL_INFO("drawinglayer", "EMF+\t Pen: " << penIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex); EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); if (path) EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex); else - SAL_WARN("drawinglayer", "\t\tEmfPlusRecordTypeDrawPath missing path"); + SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path"); break; } @@ -1393,46 +1339,98 @@ namespace emfplushelper { sal_uInt32 aCount; float x1, y1, x2, y2, x3, y3, x4, y4; - ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint; ::basegfx::B2DPolygon aPolygon; rMS.ReadUInt32(aCount); - SAL_INFO("drawinglayer", "EMF+\t DrawBeziers slot: " << (flags & 0xff)); - SAL_INFO("drawinglayer", "EMF+\t Number of points: " << aCount); - SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16..."); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff)); + SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount); + SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf", + "EMF+\t Bezier Draw not support number of points other than 4, 7, " + "10, 13, 16..."); if (aCount < 4) { - SAL_WARN("drawinglayer", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount); + SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less " + "than 4 points. Number of points: " + << aCount); break; } ReadPoint(rMS, x1, y1, flags); // We need to add first starting point - aStartPoint = Map(x1, y1); - aPolygon.append(aStartPoint); - + aPolygon.append(Map(x1, y1)); + SAL_INFO("drawinglayer.emf", + "EMF+\t Bezier starting point: " << x1 << "," << y1); for (sal_uInt32 i = 4; i <= aCount; i += 3) { ReadPoint(rMS, x2, y2, flags); ReadPoint(rMS, x3, y3, flags); ReadPoint(rMS, x4, y4, flags); - SAL_INFO("drawinglayer", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4); - - aStartPoint = Map(x1, y1); - aControlPointA = Map(x2, y2); - aControlPointB = Map(x3, y3); - aEndPoint = Map(x4, y4); - - ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint); - cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0); + SAL_INFO("drawinglayer.emf", + "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << "," + << y3 << " " << x4 << "," << y4); + aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4)); + } + EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); + break; + } + case EmfPlusRecordTypeDrawCurve: + { + sal_uInt32 aOffset, aNumSegments, points; + float aTension; + rMS.ReadFloat(aTension); + rMS.ReadUInt32(aOffset); + rMS.ReadUInt32(aNumSegments); + rMS.ReadUInt32(points); + SAL_WARN("drawinglayer.emf", + "EMF+\t Tension: " << aTension << " Offset: " << aOffset + << " NumSegments: " << aNumSegments + << " Points: " << points); - EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); + EMFPPath path(points, true); + path.Read(rMS, flags); - // The ending coordinate of one Bezier curve is the starting coordinate of the next. - x1 = x4; - y1 = y4; + if (points >= 2) + EMFPPlusDrawPolygon( + path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments), + flags & 0xff); + else + SAL_WARN("drawinglayer.emf", "Not enough number of points"); + break; + } + case EmfPlusRecordTypeDrawClosedCurve: + case EmfPlusRecordTypeFillClosedCurve: + { + // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used + sal_uInt32 brushIndexOrColor = 999, points; + float aTension; + if (type == EmfPlusRecordTypeFillClosedCurve) + { + rMS.ReadUInt32(brushIndexOrColor); + SAL_INFO( + "drawinglayer.emf", + "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate")); } + rMS.ReadFloat(aTension); + rMS.ReadUInt32(points); + SAL_WARN("drawinglayer.emf", + "EMF+\t Tension: " << aTension << " Points: " << points); + SAL_INFO("drawinglayer.emf", + "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x" + << std::hex << brushIndexOrColor << std::dec); + if (points < 3) + { + SAL_WARN("drawinglayer.emf", "Not enough number of points"); + break; + } + EMFPPath path(points, true); + path.Read(rMS, flags); + if (type == EmfPlusRecordTypeFillClosedCurve) + EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension), + flags & 0x8000, brushIndexOrColor); + else + EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension), + flags & 0xff); break; } case EmfPlusRecordTypeDrawImage: @@ -1441,20 +1439,30 @@ namespace emfplushelper sal_uInt32 imageAttributesId; sal_Int32 sourceUnit; rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit); - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " image attributes Id: " << imageAttributesId << " source unit: " << sourceUnit); - SAL_INFO("drawinglayer", "EMF+\t TODO: use image attributes"); - - // For DrawImage and DrawImagePoints, source unit of measurement type must be 1 pixel - if (sourceUnit == UnitTypePixel && maEMFPObjects[flags & 0xff]) + SAL_INFO("drawinglayer.emf", + "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage" + : "DrawImagePoints") + << " image attributes Id: " << imageAttributesId + << " source unit: " << sourceUnit); + SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes"); + + // Source unit of measurement type must be 1 pixel + if (EMFPImage* image = sourceUnit == UnitTypePixel ? + dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) : + nullptr) { - EMFPImage& image = *static_cast<EMFPImage *>(maEMFPObjects[flags & 0xff].get()); float sx, sy, sw, sh; ReadRectangle(rMS, sx, sy, sw, sh); - ::tools::Rectangle aSource(Point(sx, sy), Size(sw, sh)); - SAL_INFO("drawinglayer", "EMF+\t " << (type == EmfPlusRecordTypeDrawImagePoints ? "DrawImagePoints" : "DrawImage") << " source rectangle: " << sx << "," << sy << " " << sw << "x" << sh); - ::basegfx::B2DPoint aDstPoint; - ::basegfx::B2DSize aDstSize; + ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1)); + SAL_INFO("drawinglayer.emf", + "EMF+\t " + << (type == EmfPlusRecordTypeDrawImage ? "DrawImage" + : "DrawImagePoints") + << " source rectangle: " << sx << "," << sy << " " << sw << "x" + << sh); + + float dx(0.), dy(0.), dw(0.), dh(0.); double fShearX = 0.0; double fShearY = 0.0; if (type == EmfPlusRecordTypeDrawImagePoints) @@ -1463,251 +1471,290 @@ namespace emfplushelper rMS.ReadUInt32(aCount); // Number of points used by DrawImagePoints. Exactly 3 points must be specified. - if(aCount == 3) + if (aCount != 3) { - float x1, y1, x2, y2, x3, y3; - - ReadPoint(rMS, x1, y1, flags); // upper-left point - ReadPoint(rMS, x2, y2, flags); // upper-right - ReadPoint(rMS, x3, y3, flags); // lower-left - - SAL_INFO("drawinglayer", "EMF+\t destination points: P1:" << x1 << "," << y1 << " P2:" << x2 << "," << y2 << " P3:" << x3 << "," << y3); - - aDstPoint = ::basegfx::B2DPoint(x1, y1); - aDstSize = ::basegfx::B2DSize(x2 - x1, y3 - y1); - fShearX = x3 - x1; - fShearY = y2 - y1; - } - else - { - SAL_WARN("drawinglayer", "EMF+\t DrawImagePoints Wrong EMF+ file. Expected 3 points, received: "<< aCount); + SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected " + "3 points, received: " + << aCount); break; } + float x1, y1, x2, y2, x3, y3; + + ReadPoint(rMS, x1, y1, flags); // upper-left point + ReadPoint(rMS, x2, y2, flags); // upper-right + ReadPoint(rMS, x3, y3, flags); // lower-left + + SAL_INFO("drawinglayer.emf", "EMF+\t destination points: " + << x1 << "," << y1 << " " << x2 << "," + << y2 << " " << x3 << "," << y3); + dx = x1; + dy = y2; + dw = x2 - x1; + dh = y3 - y1; + fShearX = x3 - x1; + fShearY = y2 - y1; } else if (type == EmfPlusRecordTypeDrawImage) - { - float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); - SAL_INFO("drawinglayer", "EMF+\t destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh); - aDstPoint = ::basegfx::B2DPoint(dx, dy); - aDstSize = ::basegfx::B2DSize(dw, dh); - } - const basegfx::B2DHomMatrix aTransformMatrix = maMapTransform * - basegfx::B2DHomMatrix( - /* Row 0, Column 0 */ aDstSize.getX(), - /* Row 0, Column 1 */ fShearX, - /* Row 0, Column 2 */ aDstPoint.getX(), - /* Row 1, Column 0 */ fShearY, - /* Row 1, Column 1 */ aDstSize.getY(), - /* Row 1, Column 2 */ aDstPoint.getY()); + SAL_INFO("drawinglayer.emf", + "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh); + Size aSize; + if (image->type == ImageDataTypeBitmap) + { + aSize = image->graphic.GetBitmap().GetSizePixel(); + SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width() + << "x" + << aSize.Height()); + if (sx < 0) + { + // If src position is negative then we need shift image to right + dx = dx + ((-sx) / sw) * dw; + if (sx + sw <= aSize.Width()) + dw = ((sw + sx) / sw) * dw; + else + dw = (aSize.Width() / sw) * dw; + } + else if (sx + sw > aSize.Width()) + // If the src image is smaller that what we want to cut, then we need to scale down + dw = ((aSize.Width() - sx) / sw) * dw; - if (image.type == ImageDataTypeBitmap) + if (sy < 0) + { + dy = dy + ((-sy) / sh) * dh; + if (sy + sh <= aSize.Height()) + dh = ((sh + sy) / sh) * dh; + else + dh = (aSize.Height() / sh) * dh; + } + else if (sy + sh > aSize.Height()) + dh = ((aSize.Height() - sy) / sh) * dh; + } + else + SAL_INFO( + "drawinglayer.emf", + "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile"); + const ::basegfx::B2DPoint aDstPoint(dx, dy); + const ::basegfx::B2DSize aDstSize(dw, dh); + + const basegfx::B2DHomMatrix aTransformMatrix + = maMapTransform + * basegfx::B2DHomMatrix( + /* Row 0, Column 0 */ aDstSize.getWidth(), + /* Row 0, Column 1 */ fShearX, + /* Row 0, Column 2 */ aDstPoint.getX(), + /* Row 1, Column 0 */ fShearY, + /* Row 1, Column 1 */ aDstSize.getHeight(), + /* Row 1, Column 2 */ aDstPoint.getY()); + + if (image->type == ImageDataTypeBitmap) { - BitmapEx aBmp(image.graphic.GetBitmapEx()); + Bitmap aBmp(image->graphic.GetBitmap()); aBmp.Crop(aSource); - Size aSize(aBmp.GetSizePixel()); - SAL_INFO("drawinglayer", "EMF+\t Bitmap size: " << aSize.Width() << "x" << aSize.Height()); + aSize = aBmp.GetSizePixel(); if (aSize.Width() > 0 && aSize.Height() > 0) { mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::BitmapPrimitive2D>( - VCLUnoHelper::CreateVCLXBitmap(aBmp), - aTransformMatrix)); + new drawinglayer::primitive2d::BitmapPrimitive2D( + aBmp, aTransformMatrix)); } else - { - SAL_WARN("drawinglayer", "EMF+\t warning: empty bitmap"); - break; - } + SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap"); } - else if (image.type == ImageDataTypeMetafile) + else if (image->type == ImageDataTypeMetafile) { - GDIMetaFile aGDI(image.graphic.GetGDIMetaFile()); + GDIMetaFile aGDI(image->graphic.GetGDIMetaFile()); aGDI.Clip(aSource); mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::MetafilePrimitive2D>(aTransformMatrix, aGDI)); + new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix, + aGDI)); } } else { - SAL_WARN("drawinglayer", "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is support by EMF+ specification for DrawImage(Points)"); + SAL_WARN("drawinglayer.emf", + "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is " + "support by EMF+ specification for DrawImage(Points)"); } break; } case EmfPlusRecordTypeDrawString: { - sal_uInt32 brushId; - sal_uInt32 formatId; - sal_uInt32 stringLength; + sal_uInt32 brushId, formatId, stringLength; rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength); - SAL_INFO("drawinglayer", "EMF+\t FontId: " << OUString::number(flags & 0xFF)); - SAL_INFO("drawinglayer", "EMF+\t BrushId: " << BrushIDToString(flags, brushId)); - SAL_INFO("drawinglayer", "EMF+\t FormatId: " << formatId); - SAL_INFO("drawinglayer", "EMF+\t Length: " << stringLength); - - if (flags & 0x8000) + SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF)); + SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId)); + SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId); + SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength); + + // read the layout rectangle + float lx, ly, lw, lh; + rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh); + + SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh); + // parse the string + const OUString text = read_uInt16s_ToOUString(rMS, stringLength); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text); + // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr ) + const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get()); + // get the font from the flags + const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get()); + if (!font) { - // read the layout rectangle - float lx, ly, lw, lh; - rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh); - - SAL_INFO("drawinglayer", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh); - // parse the string - const OUString text = read_uInt16s_ToOUString(rMS, stringLength); - SAL_INFO("drawinglayer", "EMF+\t DrawString string: " << text); - // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr ) - const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get()); - // get the font from the flags - const EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); - if (!font) - { - break; - } - mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize))); + break; + } + mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize))); + + drawinglayer::attribute::FontAttribute fontAttribute( + font->family, // font family + u""_ustr, // (no) font style + font->Bold() ? 8u : 1u, // weight: 8 = bold + font->family == "SYMBOL", // symbol + stringFormat && stringFormat->DirectionVertical(), // vertical + font->Italic(), // italic + false, // monospaced + false, // outline = false, no such thing in MS-EMFPLUS + stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left + false); // BiDiStrong + + css::lang::Locale locale; + double stringAlignmentHorizontalOffset = 0.0; + double stringAlignmentVerticalOffset = font->emSize; + if (stringFormat) + { + LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language)); + locale = aLanguageTag.getLocale(); + drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; - drawinglayer::attribute::FontAttribute fontAttribute( - font->family, // font family - "", // (no) font style - font->Bold() ? 8u : 1u, // weight: 8 = bold - font->family == "SYMBOL", // symbol - stringFormat && stringFormat->DirectionVertical(), // vertical - font->Italic(), // italic - false, // monospaced - false, // outline = false, no such thing in MS-EMFPLUS - stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left - false); // BiDiStrong - - css::lang::Locale locale; - double stringAlignmentHorizontalOffset = 0.0; - if (stringFormat) - { - SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer", "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right."); - if (stringFormat->stringAlignment == StringAlignmentNear) + aTextLayouter.setFontAttribute(fontAttribute, font->emSize, + font->emSize, locale); + + double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength); + SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf", + "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right."); + if (stringFormat->stringAlignment == StringAlignmentNear) // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh) - { - stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize; - } else if (stringFormat->stringAlignment == StringAlignmentCenter) + stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize; + else if (stringFormat->stringAlignment == StringAlignmentCenter) // Alignment is centered between the origin and extent of the layout rectangle - { - stringAlignmentHorizontalOffset = 0.5 * lw + stringFormat->leadingMargin * font->emSize - 0.3 * font->emSize * stringLength; - } else if (stringFormat->stringAlignment == StringAlignmentFar) + stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth; + else if (stringFormat->stringAlignment == StringAlignmentFar) // Alignment is to the right side of the layout rectangle - { - stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - 0.6 * font->emSize * stringLength; - } + stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth; + + if (stringFormat->lineAlign == StringAlignmentNear) + stringAlignmentVerticalOffset = font->emSize; + else if (stringFormat->lineAlign == StringAlignmentCenter) + stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize; + else if (stringFormat->lineAlign == StringAlignmentFar) + stringAlignmentVerticalOffset = lh; + } + else + { + // By default LeadingMargin is 1/6 inch + // TODO for typographic fonts set value to 0. + stringAlignmentHorizontalOffset = 16.0; - LanguageTag aLanguageTag(static_cast< LanguageType >(stringFormat->language)); - locale = aLanguageTag.getLocale(); - } - else - { - // By default LeadingMargin is 1/6 inch - // TODO for typographic fonts set value to 0. - stringAlignmentHorizontalOffset = 16.0; + // use system default + locale = Application::GetSettings().GetLanguageTag().getLocale(); + } - // use system default - locale = Application::GetSettings().GetLanguageTag().getLocale(); - } + const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( + ::basegfx::B2DVector(font->emSize, font->emSize), + ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, + ly + stringAlignmentVerticalOffset)); + + Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId); + Color color; - const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( - ::basegfx::B2DSize(font->emSize, font->emSize), - ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, ly + font->emSize)); + if (mbSetTextContrast) + { + const auto gammaVal = mnTextContrast / 1000; + const basegfx::BColorModifier_gamma gamma(gammaVal); + + // gamma correct transparency color + sal_uInt16 alpha = uncorrectedColor.GetAlpha(); + alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255; + + basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor())); + color.SetRed(modifiedColor.getRed() * 255); + color.SetGreen(modifiedColor.getGreen() * 255); + color.SetBlue(modifiedColor.getBlue() * 255); + color.SetAlpha(alpha); + } + else + { + color = uncorrectedColor; + } - Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId); - Color color; + mrPropertyHolders.Current().setTextColor(color.getBColor()); + mrPropertyHolders.Current().setTextColorActive(true); - if (mbSetTextContrast) + if (color.GetAlpha() > 0) + { + std::vector<double> emptyVector; + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText; + if (font->Underline() || font->Strikeout()) { - const auto gammaVal = mnTextContrast / 1000; - const basegfx::BColorModifier_gamma gamma(gammaVal); - - // gamma correct transparency color - sal_uInt16 alpha = uncorrectedColor.GetTransparency(); - alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255; - - basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor())); - color.SetRed(modifiedColor.getRed() * 255); - color.SetGreen(modifiedColor.getGreen() * 255); - color.SetBlue(modifiedColor.getBlue() * 255); - color.SetTransparency(alpha); + pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( + transformMatrix, + text, + 0, // text always starts at 0 + stringLength, + std::move(emptyVector), // EMF-PLUS has no DX-array + {}, + std::move(fontAttribute), + locale, + color.getBColor(), // Font Color + COL_TRANSPARENT, // Fill Color + color.getBColor(), // OverlineColor + color.getBColor(), // TextlineColor + drawinglayer::primitive2d::TEXT_LINE_NONE, + font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, + false, + font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); } else { - color = uncorrectedColor; + pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + transformMatrix, + text, + 0, // text always starts at 0 + stringLength, + std::move(emptyVector), // EMF-PLUS has no DX-array + {}, + std::move(fontAttribute), + std::move(locale), + color.getBColor()); } - - mrPropertyHolders.Current().setTextColor(color.getBColor()); - mrPropertyHolders.Current().setTextColorActive(true); - - if (color.GetTransparency() < 255) + drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); + if (color.IsTransparent()) { - std::vector<double> emptyVector; - drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; - if (font->Underline() || font->Strikeout()) - { - pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( - transformMatrix, - text, - 0, // text always starts at 0 - stringLength, - emptyVector, // EMF-PLUS has no DX-array - fontAttribute, - locale, - color.getBColor(), - COL_TRANSPARENT, - color.getBColor(), - color.getBColor(), - drawinglayer::primitive2d::TEXT_LINE_NONE, - font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, - false, - font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); - } - else - { - pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( - transformMatrix, - text, - 0, // text always starts at 0 - stringLength, - emptyVector, // EMF-PLUS has no DX-array - fontAttribute, - locale, - color.getBColor()); - } - drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); - if (color.GetTransparency() != 0) - { - aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( - drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, - color.GetTransparency() / 255.0); - } - - mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( - maMapTransform, - drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); + aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, + (255 - color.GetAlpha()) / 255.0); } - } - else - { - SAL_WARN("drawinglayer", "EMF+\t DrawString TODO - drawing with brush not yet supported"); + + mrTargetHolders.Current().append( + new drawinglayer::primitive2d::TransformPrimitive2D( + maMapTransform, + drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); } break; } case EmfPlusRecordTypeSetPageTransform: { rMS.ReadFloat(mfPageScale); - SAL_INFO("drawinglayer", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags)); + SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags)); if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) { - SAL_WARN("drawinglayer", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification."); + SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification."); } else { - mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI); - mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI); + mnMmX = std::round(unitToPixel(static_cast<double>(mnMmX) * mfPageScale, flags, Direction::horizontal)); + mnMmY = std::round(unitToPixel(static_cast<double>(mnMmY) * mfPageScale, flags, Direction::vertical)); mappingChanged(); } break; @@ -1715,7 +1762,7 @@ namespace emfplushelper case EmfPlusRecordTypeSetRenderingOrigin: { rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY); - SAL_INFO("drawinglayer", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); + SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); break; } case EmfPlusRecordTypeSetTextContrast: @@ -1726,51 +1773,51 @@ namespace emfplushelper mbSetTextContrast = true; mnTextContrast = flags & 0xFFF; SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA, - "drawinglayer", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast); + "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast); mnTextContrast = std::min(mnTextContrast, UPPERGAMMA); mnTextContrast = std::max(mnTextContrast, LOWERGAMMA); - SAL_INFO("drawinglayer", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma"); + SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma"); break; } case EmfPlusRecordTypeSetTextRenderingHint: { sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1; - SAL_INFO("drawinglayer", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetTextRenderingHint"); + SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint)); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint"); break; } case EmfPlusRecordTypeSetAntiAliasMode: { bool bUseAntiAlias = (flags & 0x0001); sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1; - SAL_INFO("drawinglayer", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled")); - SAL_INFO("drawinglayer", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetAntiAliasMode"); + SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled")); + SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode)); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode"); break; } case EmfPlusRecordTypeSetInterpolationMode: { sal_uInt16 nInterpolationMode = flags & 0xFF; - SAL_INFO("drawinglayer", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode)); - SAL_WARN("drawinglayer", "EMF+\t TODO InterpolationMode"); + SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode)); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode"); break; } case EmfPlusRecordTypeSetPixelOffsetMode: { - SAL_INFO("drawinglayer", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags)); - SAL_WARN("drawinglayer", "EMF+\t TODO SetPixelOffsetMode"); + SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags)); + SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode"); break; } case EmfPlusRecordTypeSetCompositingQuality: { - SAL_INFO("drawinglayer", "EMF+\t TODO SetCompositingQuality"); + SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality"); break; } case EmfPlusRecordTypeSave: { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Save stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex); GraphicStatePush(mGSStack, stackIndex); @@ -1780,36 +1827,36 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Restore stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex); - GraphicStatePop(mGSStack, stackIndex, mrPropertyHolders.Current()); + GraphicStatePop(mGSStack, stackIndex); break; } case EmfPlusRecordTypeBeginContainer: { float dx, dy, dw, dh; ReadRectangle(rMS, dx, dy, dw, dh); - SAL_INFO("drawinglayer", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh); + SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh); float sx, sy, sw, sh; ReadRectangle(rMS, sx, sy, sw, sh); - SAL_INFO("drawinglayer", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh); + SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh); sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags); + SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags); if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) { - SAL_WARN("drawinglayer", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification."); + SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification."); break; } - const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI); - const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI); GraphicStatePush(mGSContainerStack, stackIndex); const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix( - aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ), - aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) ); + unitToPixel(static_cast<double>(dw) / sw, flags, Direction::horizontal), + unitToPixel(static_cast<double>(dh) / sh, flags, Direction::vertical), + unitToPixel(static_cast<double>(dx) - sx, flags, Direction::horizontal), + unitToPixel(static_cast<double>(dy) - sy, flags, Direction::vertical)); maWorldTransform *= transform; mappingChanged(); break; @@ -1818,7 +1865,7 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t Begin Container No Params stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex); GraphicStatePush(mGSContainerStack, stackIndex); break; @@ -1827,33 +1874,33 @@ namespace emfplushelper { sal_uInt32 stackIndex; rMS.ReadUInt32(stackIndex); - SAL_INFO("drawinglayer", "EMF+\t End Container stack index: " << stackIndex); + SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex); - GraphicStatePop(mGSContainerStack, stackIndex, mrPropertyHolders.Current()); + GraphicStatePop(mGSContainerStack, stackIndex); break; } case EmfPlusRecordTypeSetWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); readXForm(rMS, maWorldTransform); mappingChanged(); - SAL_INFO("drawinglayer", "EMF+\t\t: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform); break; } case EmfPlusRecordTypeResetWorldTransform: { maWorldTransform.identity(); - SAL_INFO("drawinglayer", "EMF+\t World transform: " << maWorldTransform); + SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform); mappingChanged(); break; } case EmfPlusRecordTypeMultiplyWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); basegfx::B2DHomMatrix transform; readXForm(rMS, transform); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t Transform matrix: " << transform); if (flags & 0x2000) @@ -1870,13 +1917,13 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } case EmfPlusRecordTypeTranslateWorldTransform: { - SAL_INFO("drawinglayer", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); + SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); basegfx::B2DHomMatrix transform; float eDx, eDy; @@ -1884,7 +1931,7 @@ namespace emfplushelper transform.set(0, 2, eDx); transform.set(1, 2, eDy); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t Translate matrix: " << transform); if (flags & 0x2000) @@ -1901,7 +1948,7 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } @@ -1913,9 +1960,9 @@ namespace emfplushelper transform.set(0, 0, eSx); transform.set(1, 1, eSy); - SAL_INFO("drawinglayer", "EMF+\t ScaleWorldTransform Sx: " << eSx << + SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx << " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000)); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); if (flags & 0x2000) @@ -1932,7 +1979,7 @@ namespace emfplushelper mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t World transform matrix: " << maWorldTransform); break; } @@ -1942,20 +1989,20 @@ namespace emfplushelper float eAngle; rMS.ReadFloat(eAngle); - SAL_INFO("drawinglayer", "EMF+\t RotateWorldTransform Angle: " << eAngle << + SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle << ", post multiply: " << bool(flags & 0x2000)); // Skipping flags & 0x2000 // For rotation transformation there is no difference between post and pre multiply maWorldTransform.rotate(basegfx::deg2rad(eAngle)); mappingChanged(); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t " << maWorldTransform); break; } case EmfPlusRecordTypeResetClip: { - SAL_INFO("drawinglayer", "EMF+ ResetClip"); + SAL_INFO("drawinglayer.emf", "EMF+ ResetClip"); // We don't need to read anything more, as Size needs to be set 0x0000000C // and DataSize must be set to 0. @@ -1964,74 +2011,107 @@ namespace emfplushelper break; } case EmfPlusRecordTypeSetClipRect: - { - int combineMode = (flags >> 8) & 0xf; - - SAL_INFO("drawinglayer", "EMF+\t SetClipRect combine mode: " << combineMode); - - float dx, dy, dw, dh; - ReadRectangle(rMS, dx, dy, dw, dh); - SAL_INFO("drawinglayer", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); - ::basegfx::B2DPoint mappedPoint1(Map(dx, dy)); - ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh)); - - ::basegfx::B2DPolyPolygon polyPolygon( - ::basegfx::utils::createPolygonFromRect( - ::basegfx::B2DRectangle( - mappedPoint1.getX(), - mappedPoint1.getY(), - mappedPoint2.getX(), - mappedPoint2.getY()))); - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, polyPolygon), mrTargetHolders, mrPropertyHolders); - break; - } case EmfPlusRecordTypeSetClipPath: + case EmfPlusRecordTypeSetClipRegion: { int combineMode = (flags >> 8) & 0xf; - SAL_INFO("drawinglayer", "EMF+\t SetClipPath combine mode: " << combineMode); - SAL_INFO("drawinglayer", "EMF+\t Path in slot: " << (flags & 0xff)); - - EMFPPath *path = static_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); - if (!path) + ::basegfx::B2DPolyPolygon polyPolygon; + if (type == EmfPlusRecordTypeSetClipRect) { - SAL_WARN("drawinglayer", "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff)); - break; + SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect"); + + float dx, dy, dw, dh; + ReadRectangle(rMS, dx, dy, dw, dh); + SAL_INFO("drawinglayer.emf", + "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); + ::basegfx::B2DPoint mappedPoint1(Map(dx, dy)); + ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh)); + + polyPolygon + = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect( + ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(), + mappedPoint2.getX(), mappedPoint2.getY()))); } + else if (type == EmfPlusRecordTypeSetClipPath) + { + SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff)); - ::basegfx::B2DPolyPolygon& clipPoly(path->GetPolygon(*this)); - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, clipPoly), mrTargetHolders, mrPropertyHolders); - break; - } - case EmfPlusRecordTypeSetClipRegion: - { - int combineMode = (flags >> 8) & 0xf; - SAL_INFO("drawinglayer", "EMF+\t Region in slot: " << (flags & 0xff)); - SAL_INFO("drawinglayer", "EMF+\t Combine mode: " << combineMode); - EMFPRegion *region = static_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); - if (!region) + EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); + if (!path) + { + SAL_WARN("drawinglayer.emf", + "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff)); + break; + } + polyPolygon = path->GetPolygon(*this); + } + else if (type == EmfPlusRecordTypeSetClipRegion) { - SAL_WARN("drawinglayer", "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff)); - break; + SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff)); + EMFPRegion* region + = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); + if (!region) + { + SAL_WARN( + "drawinglayer.emf", + "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff)); + break; + } + polyPolygon = region->regionPolyPolygon; } - - HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), - combineMode, region->regionPolyPolygon), mrTargetHolders, mrPropertyHolders); + SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode); + ::basegfx::B2DPolyPolygon aClippedPolyPolygon; + if (mrPropertyHolders.Current().getClipPolyPolygonActive()) + { + aClippedPolyPolygon + = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), + combineMode, polyPolygon); + } + else + { + //Combine with infinity + switch (combineMode) + { + case EmfPlusCombineModeReplace: + case EmfPlusCombineModeIntersect: + { + aClippedPolyPolygon = polyPolygon; + break; + } + case EmfPlusCombineModeUnion: + { + // Disable clipping as the clipping is infinity + aClippedPolyPolygon = ::basegfx::B2DPolyPolygon(); + break; + } + case EmfPlusCombineModeXOR: + case EmfPlusCombineModeComplement: + { + //TODO It is not correct and it should be fixed + aClippedPolyPolygon = std::move(polyPolygon); + break; + } + case EmfPlusCombineModeExclude: + { + //TODO It is not correct and it should be fixed + aClippedPolyPolygon = ::basegfx::B2DPolyPolygon(); + break; + } + } + } + HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders); break; } case EmfPlusRecordTypeOffsetClip: { float dx, dy; rMS.ReadFloat(dx).ReadFloat(dy); - SAL_INFO("drawinglayer", "EMF+\tOffset x:" << dx << ", y:" << dy); + SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy); basegfx::B2DPolyPolygon aPolyPolygon( mrPropertyHolders.Current().getClipPolyPolygon()); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t PolyPolygon before translate: " << aPolyPolygon); basegfx::B2DPoint aOffset = Map(dx, dy); @@ -2040,7 +2120,7 @@ namespace emfplushelper transformMatrix.set(1, 2, aOffset.getY()); aPolyPolygon.transform(transformMatrix); - SAL_INFO("drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\t PolyPolygon after translate: " << aPolyPolygon << ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY()); HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders); @@ -2053,22 +2133,22 @@ namespace emfplushelper sal_uInt32 hasMatrix; sal_uInt32 glyphsCount; rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount); - SAL_INFO("drawinglayer", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); - SAL_INFO("drawinglayer", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec); - SAL_INFO("drawinglayer", "EMF+\t Has matrix: " << hasMatrix); - SAL_INFO("drawinglayer", "EMF+\t Glyphs: " << glyphsCount); + SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix); + SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount); if ((optionFlags & 1) && glyphsCount > 0) { std::unique_ptr<float[]> charsPosX(new float[glyphsCount]); std::unique_ptr<float[]> charsPosY(new float[glyphsCount]); OUString text = read_uInt16s_ToOUString(rMS, glyphsCount); - SAL_INFO("drawinglayer", "EMF+\t DrawDriverString string: " << text); + SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text); for (sal_uInt32 i = 0; i<glyphsCount; i++) { rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]); - SAL_INFO("drawinglayer", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); } basegfx::B2DHomMatrix transform; @@ -2076,11 +2156,11 @@ namespace emfplushelper if (hasMatrix) { readXForm(rMS, transform); - SAL_INFO("drawinglayer", "EMF+\tmatrix: " << transform); + SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform); } // get the font from the flags - EMFPFont *font = static_cast< EMFPFont* >( maEMFPObjects[flags & 0xff].get() ); + EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get()); if (!font) { break; @@ -2089,7 +2169,7 @@ namespace emfplushelper drawinglayer::attribute::FontAttribute fontAttribute( font->family, // font family - "", // (no) font style + u""_ustr, // (no) font style font->Bold() ? 8u : 1u, // weight: 8 = bold font->family == "SYMBOL", // symbol optionFlags & 0x2, // vertical @@ -2100,7 +2180,6 @@ namespace emfplushelper false); // BiDiStrong const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor); - std::vector<double> aDXArray; // dummy for DX array (not used) // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D // for all portions of text with the same charsPosY values @@ -2113,7 +2192,7 @@ namespace emfplushelper aLength++; // generate the DX-Array - aDXArray.clear(); + std::vector<double> aDXArray; for (size_t i = 0; i < aLength - 1; i++) { aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]); @@ -2122,13 +2201,13 @@ namespace emfplushelper aDXArray.push_back(0); basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( - ::basegfx::B2DSize(font->emSize, font->emSize), + ::basegfx::B2DVector(font->emSize, font->emSize), ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos])); if (hasMatrix) transformMatrix *= transform; - if (color.GetTransparency() < 255) + if (color.GetAlpha() > 0) { - drawinglayer::primitive2d::BasePrimitive2D* pBaseText = nullptr; + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText; if (font->Underline() || font->Strikeout()) { pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( @@ -2136,7 +2215,8 @@ namespace emfplushelper text, pos, // take character at current pos aLength, // use determined length - aDXArray, // generated DXArray + std::move(aDXArray), // generated DXArray + {}, fontAttribute, Application::GetSettings().GetLanguageTag().getLocale(), color.getBColor(), @@ -2155,20 +2235,21 @@ namespace emfplushelper text, pos, // take character at current pos aLength, // use determined length - aDXArray, // generated DXArray + std::move(aDXArray), // generated DXArray + {}, fontAttribute, Application::GetSettings().GetLanguageTag().getLocale(), color.getBColor()); } drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); - if (color.GetTransparency() != 0) + if (color.IsTransparent()) { aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, - color.GetTransparency() / 255.0); + (255 - color.GetAlpha()) / 255.0); } mrTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( maMapTransform, drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); } @@ -2179,13 +2260,13 @@ namespace emfplushelper } else { - SAL_WARN("drawinglayer", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); + SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); } break; } default: { - SAL_WARN("drawinglayer", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); } } } @@ -2198,7 +2279,7 @@ namespace emfplushelper } else { - SAL_WARN("drawinglayer", "ImplRenderer::processEMFPlus: " + SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: " "size " << size << " > length " << length); length = 0; } diff --git a/drawinglayer/source/tools/emfphelperdata.hxx b/drawinglayer/source/tools/emfphelperdata.hxx index 500ceb323b02..51561e3161fa 100644 --- a/drawinglayer/source/tools/emfphelperdata.hxx +++ b/drawinglayer/source/tools/emfphelperdata.hxx @@ -17,17 +17,16 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPHELPERDATA_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPHELPERDATA_HXX +#pragma once #include <wmfemfhelper.hxx> #include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/attribute/linestartendattribute.hxx> #include <tools/stream.hxx> #include <basegfx/point/b2dpoint.hxx> #include <map> // predefines -class SvStream; namespace basegfx { class B2DPolyPolygon; } namespace emfplushelper @@ -54,9 +53,9 @@ namespace emfplushelper #define EmfPlusRecordTypeFillRegion 0x4013 #define EmfPlusRecordTypeFillPath 0x4014 #define EmfPlusRecordTypeDrawPath 0x4015 - //TODO EmfPlusRecordTypeFillClosedCurve 0x4016 - //TODO EmfPlusRecordTypeDrawClosedCurve 0x4017 - //TODO EmfPlusRecordTypeDrawCurve 0x4018 + #define EmfPlusRecordTypeFillClosedCurve 0x4016 + #define EmfPlusRecordTypeDrawClosedCurve 0x4017 + #define EmfPlusRecordTypeDrawCurve 0x4018 #define EmfPlusRecordTypeDrawBeziers 0x4019 #define EmfPlusRecordTypeDrawImage 0x401A #define EmfPlusRecordTypeDrawImagePoints 0x401B @@ -211,9 +210,15 @@ namespace emfplushelper GraphicStateMap mGSStack; GraphicStateMap mGSContainerStack; + /* Performance optimizators */ + /* Extracted Scale values from Transformation Matrix */ + double mdExtractedXScale; + double mdExtractedYScale; + /// data holders wmfemfhelper::TargetHolders& mrTargetHolders; wmfemfhelper::PropertyHolders& mrPropertyHolders; + wmfemfhelper::PropertyHolder aGetDCState; bool bIsGetDCProcessing; // readers @@ -225,7 +230,10 @@ namespace emfplushelper // stack actions void GraphicStatePush(GraphicStateMap& map, sal_Int32 index); - void GraphicStatePop (GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState); + void GraphicStatePop(GraphicStateMap& map, sal_Int32 index); + + drawinglayer::attribute::LineStartEndAttribute CreateLineEnd(const sal_Int32 aCap, + const float aPenWidth) const; // primitive creators void EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex); @@ -235,6 +243,14 @@ namespace emfplushelper // helper functions Color EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const; + enum class Direction + { + horizontal, + vertical + }; + sal_uInt32 DPI(Direction d) { return d == Direction::horizontal ? mnHDPI : mnVDPI; } + double unitToPixel(double n, sal_uInt32 aUnitType, Direction d); + public: EmfPlusHelperData( SvMemoryStream& rMS, @@ -253,11 +269,7 @@ namespace emfplushelper static void ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed = false); static bool readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget); static ::basegfx::B2DPolyPolygon combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon); - - static float getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI); }; } -#endif // INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPHELPERDATA_HXX - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpimage.cxx b/drawinglayer/source/tools/emfpimage.cxx index 502eb2b74f0f..67a0cef99ed2 100644 --- a/drawinglayer/source/tools/emfpimage.cxx +++ b/drawinglayer/source/tools/emfpimage.cxx @@ -26,20 +26,20 @@ namespace emfplushelper { sal_uInt32 header, bitmapType; s.ReadUInt32(header).ReadUInt32(type); - SAL_INFO("drawinglayer", "EMF+\timage\nEMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\timage\nEMF+\theader: 0x" << std::hex << header << " type: " << type << std::dec); if (ImageDataTypeBitmap == type) { // bitmap s.ReadInt32(width).ReadInt32(height).ReadInt32(stride).ReadUInt32(pixelFormat).ReadUInt32(bitmapType); - SAL_INFO("drawinglayer", "EMF+\tbitmap width: " << width << " height: " << height << " stride: " << stride << " pixelFormat: 0x" << std::hex << pixelFormat << " bitmapType: 0x" << bitmapType << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\tbitmap width: " << width << " height: " << height << " stride: " << stride << " pixelFormat: 0x" << std::hex << pixelFormat << " bitmapType: 0x" << bitmapType << std::dec); if ((bitmapType != 0) || (width == 0)) { // non native formats GraphicFilter filter; - filter.ImportGraphic(graphic, OUString(), s); - SAL_INFO("drawinglayer", "EMF+\tbitmap width: " << graphic.GetSizePixel().Width() << " height: " << graphic.GetSizePixel().Height()); + filter.ImportGraphic(graphic, u"", s); + SAL_INFO("drawinglayer.emf", "EMF+\tbitmap width: " << graphic.GetSizePixel().Width() << " height: " << graphic.GetSizePixel().Height()); } } else if (ImageDataTypeMetafile == type) @@ -53,12 +53,12 @@ namespace emfplushelper else dataSize -= 16; - SAL_INFO("drawinglayer", "EMF+\tmetafile type: " << mfType << " dataSize: " << mfSize << " real size calculated from record dataSize: " << dataSize); + SAL_INFO("drawinglayer.emf", "EMF+\tmetafile type: " << mfType << " dataSize: " << mfSize << " real size calculated from record dataSize: " << dataSize); GraphicFilter filter; // workaround buggy metafiles, which have wrong mfSize set (n#705956 for example) SvMemoryStream mfStream(const_cast<char *>(static_cast<char const *>(s.GetData()) + s.Tell()), dataSize, StreamMode::READ); - filter.ImportGraphic(graphic, OUString(), mfStream); + filter.ImportGraphic(graphic, u"", mfStream); // debug code - write the stream to debug file /tmp/emf-stream.emf #if OSL_DEBUG_LEVEL > 1 diff --git a/drawinglayer/source/tools/emfpimage.hxx b/drawinglayer/source/tools/emfpimage.hxx index ed656e2c33db..75e42e7d21e4 100644 --- a/drawinglayer/source/tools/emfpimage.hxx +++ b/drawinglayer/source/tools/emfpimage.hxx @@ -17,8 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGE_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGE_HXX +#pragma once #include "emfphelperdata.hxx" #include <vcl/graph.hxx> @@ -46,6 +45,4 @@ namespace emfplushelper }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpimageattributes.cxx b/drawinglayer/source/tools/emfpimageattributes.cxx index 8508172fe6a5..11c1f47173e4 100644 --- a/drawinglayer/source/tools/emfpimageattributes.cxx +++ b/drawinglayer/source/tools/emfpimageattributes.cxx @@ -22,14 +22,12 @@ #include "emfpimageattributes.hxx" using namespace ::com::sun::star; -using namespace ::basegfx; namespace emfplushelper { EMFPImageAttributes::EMFPImageAttributes() : EMFPObject() , wrapMode(0) - , clampColor(Color()) , objectClamp(0) { } @@ -56,18 +54,18 @@ void EMFPImageAttributes::Read(SvStream& s) clampColor.SetRed(clampColorRed); clampColor.SetGreen(clampColorGreen); clampColor.SetBlue(clampColorBlue); - clampColor.SetTransparency(clampColorAlpha); + clampColor.SetAlpha(255 - clampColorAlpha); - SAL_INFO("drawinglayer", "EMF+\timage attributes"); - SAL_WARN_IF((reserved1 != 0) || (reserved2 != 0), "drawinglayer", + SAL_INFO("drawinglayer.emf", "EMF+\timage attributes"); + SAL_WARN_IF((reserved1 != 0) || (reserved2 != 0), "drawinglayer.emf", "Reserved field(s) not zero - reserved1: " << std::hex << reserved1 << " reserved2: " << reserved2); SAL_WARN_IF((objectClamp != EmpPlusRectClamp) && (objectClamp != EmpPlusBitmapClamp), - "drawinglayer", "Invalid object clamp - set to" << std::hex << objectClamp); - SAL_INFO("drawinglayer", "EMF+\t image graphics version: 0x" - << std::hex << graphicsVersion << " wrap mode: " << wrapMode - << " clamp color: " << clampColor - << " object clamp: " << objectClamp); + "drawinglayer.emf", "Invalid object clamp - set to" << std::hex << objectClamp); + SAL_INFO("drawinglayer.emf", "EMF+\t image graphics version: 0x" + << std::hex << graphicsVersion << " wrap mode: " << wrapMode + << " clamp color: " << clampColor + << " object clamp: " << objectClamp); } } diff --git a/drawinglayer/source/tools/emfpimageattributes.hxx b/drawinglayer/source/tools/emfpimageattributes.hxx index 0ac76938c9e7..a8ba375538b5 100644 --- a/drawinglayer/source/tools/emfpimageattributes.hxx +++ b/drawinglayer/source/tools/emfpimageattributes.hxx @@ -7,11 +7,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGEATTRIBUTES_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPIMAGEATTRIBUTES_HXX +#pragma once #include "emfphelperdata.hxx" -#include <vector> namespace emfplushelper { @@ -32,6 +30,4 @@ struct EMFPImageAttributes : public EMFPObject }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfppath.cxx b/drawinglayer/source/tools/emfppath.cxx index 1f16c292cad8..cf841fd999e0 100644 --- a/drawinglayer/source/tools/emfppath.cxx +++ b/drawinglayer/source/tools/emfppath.cxx @@ -20,7 +20,6 @@ #include <basegfx/point/b2dpoint.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolypolygon.hxx> -#include <o3tl/safeint.hxx> #include <sal/log.hxx> #include "emfppath.hxx" @@ -35,6 +34,13 @@ namespace namespace emfplushelper { + typedef double matrix [4][4]; + + constexpr sal_uInt32 nDetails = 8; + constexpr double alpha[nDetails] + = { 1. / nDetails, 2. / nDetails, 3. / nDetails, 4. / nDetails, + 5. / nDetails, 6. / nDetails, 7. / nDetails, 8. / nDetails }; + // see 2.2.2.21 EmfPlusInteger7 // 2.2.2.22 EmfPlusInteger15 // and 2.2.2.37 EmfPlusPointR Object @@ -58,15 +64,14 @@ namespace emfplushelper return static_cast<sal_Int16>(nRet); } - EMFPPath::EMFPPath (sal_Int32 _nPoints, bool bLines) + EMFPPath::EMFPPath (sal_uInt32 _nPoints, bool bLines) { - if (_nPoints<0 || o3tl::make_unsigned(_nPoints)>SAL_MAX_INT32 / (2 * sizeof(float))) + if (_nPoints > SAL_MAX_UINT32 / (2 * sizeof(float))) { - _nPoints = SAL_MAX_INT32 / (2 * sizeof(float)); + _nPoints = SAL_MAX_UINT32 / (2 * sizeof(float)); } nPoints = _nPoints; - pPoints.reset( new float [nPoints*2] ); if (!bLines) pPointTypes.reset( new sal_uInt8 [_nPoints] ); @@ -78,7 +83,8 @@ namespace emfplushelper void EMFPPath::Read (SvStream& s, sal_uInt32 pathFlags) { - for (int i = 0; i < nPoints; i ++) + float fx, fy; + for (sal_uInt32 i = 0; i < nPoints; i++) { if (pathFlags & 0x800) { @@ -87,34 +93,36 @@ namespace emfplushelper // If 0x800 bit is set, the 0x4000 bit is undefined and must be ignored sal_Int32 x = GetEmfPlusInteger(s); sal_Int32 y = GetEmfPlusInteger(s); - pPoints [i*2] = x; - pPoints [i*2 + 1] = y; - SAL_INFO("drawinglayer", "EMF+\t\t\tEmfPlusPointR [x,y]: " << x << ", " << y); + xPoints.push_back(x); + yPoints.push_back(y); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPointR [x,y]: " << x << ", " << y); } else if (pathFlags & 0x4000) { // EMFPlusPoint: stored in signed short 16bit integer format sal_Int16 x, y; - s.ReadInt16( x ).ReadInt16( y ); - SAL_INFO ("drawinglayer", "EMF+\t\t\tEmfPlusPoint [x,y]: " << x << "," << y); - pPoints [i*2] = x; - pPoints [i*2 + 1] = y; + s.ReadInt16(x).ReadInt16(y); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t" << i << ". EmfPlusPoint [x,y]: " << x << ", " << y); + xPoints.push_back(x); + yPoints.push_back(y); } else { // EMFPlusPointF: stored in Single (float) format - s.ReadFloat( pPoints [i*2] ).ReadFloat( pPoints [i*2 + 1] ); - SAL_INFO ("drawinglayer", "EMF+\t EMFPlusPointF [x,y]: " << pPoints [i*2] << "," << pPoints [i*2 + 1]); + s.ReadFloat(fx).ReadFloat(fy); + SAL_INFO("drawinglayer.emf", "EMF+\t" << i << ". EMFPlusPointF [x,y]: " << fx << ", " << fy); + xPoints.push_back(fx); + yPoints.push_back(fy); } } if (pPointTypes) { - for (int i = 0; i < nPoints; i++) + for (sal_uInt32 i = 0; i < nPoints; i++) { s.ReadUChar(pPointTypes[i]); - SAL_INFO("drawinglayer", "EMF+\tpoint type: " << static_cast<int>(pPointTypes[i])); + SAL_INFO("drawinglayer.emf", "EMF+\tpoint type: 0x" << std::hex << static_cast<int>(pPointTypes[i]) << std::dec); } } @@ -125,11 +133,11 @@ namespace emfplushelper { ::basegfx::B2DPolygon polygon; aPolygon.clear (); - int last_normal = 0, p = 0; + sal_uInt32 last_normal = 0, p = 0; ::basegfx::B2DPoint prev, mapped; bool hasPrev = false; - for (int i = 0; i < nPoints; i ++) + for (sal_uInt32 i = 0; i < nPoints; i++) { if (p && pPointTypes && (pPointTypes [i] == 0)) { @@ -140,9 +148,9 @@ namespace emfplushelper } if (bMapIt) - mapped = rR.Map (pPoints [i*2], pPoints [i*2 + 1]); + mapped = rR.Map(xPoints[i], yPoints [i]); else - mapped = ::basegfx::B2DPoint (pPoints [i*2], pPoints [i*2 + 1]); + mapped = ::basegfx::B2DPoint(xPoints[i], yPoints[i]); if (pPointTypes) { @@ -150,8 +158,9 @@ namespace emfplushelper { if (((i - last_normal )% 3) == 1) { + assert(p != 0); polygon.setNextControlPoint (p - 1, mapped); - SAL_INFO ("drawinglayer", "EMF+\t\tPolygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append next: " << p - 1 << " mapped: " << mapped.getX () << "," << mapped.getY ()); continue; } else if (((i - last_normal) % 3) == 2) @@ -168,23 +177,22 @@ namespace emfplushelper } polygon.append (mapped); - SAL_INFO ("drawinglayer", "EMF+\t\tPolygon append point: " << pPoints [i*2] << "," << pPoints [i*2 + 1] << " mapped: " << mapped.getX () << ":" << mapped.getY ()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\tPoint: " << xPoints[i] << "," << yPoints[i] << " mapped: " << mapped.getX () << ":" << mapped.getY ()); if (hasPrev) { polygon.setPrevControlPoint (p, prev); - SAL_INFO ("drawinglayer", "EMF+\t\tPolygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon append prev: " << p << " mapped: " << prev.getX () << "," << prev.getY ()); hasPrev = false; } - p ++; + p++; - if (pPointTypes && (pPointTypes [i] & 0x80)) + if (pPointTypes && (pPointTypes [i] & 0x80)) // closed polygon { - // closed polygon polygon.setClosed (true); aPolygon.append (polygon); - SAL_INFO ("drawinglayer", "EMF+\t\tClose polygon"); + SAL_INFO ("drawinglayer.emf", "EMF+\t\tClose polygon"); last_normal = i + 1; p = 0; polygon.clear (); @@ -204,17 +212,17 @@ namespace emfplushelper #if OSL_DEBUG_LEVEL > 1 for (unsigned int i=0; i<aPolygon.count(); i++) { polygon = aPolygon.getB2DPolygon(i); - SAL_INFO ("drawinglayer", "EMF+\t\tPolygon: " << i); + SAL_INFO ("drawinglayer.emf", "EMF+\t\tPolygon: " << i); for (unsigned int j=0; j<polygon.count(); j++) { ::basegfx::B2DPoint point = polygon.getB2DPoint(j); - SAL_INFO ("drawinglayer", "EMF+\t\t\tPoint: " << point.getX() << "," << point.getY()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPoint: " << point.getX() << "," << point.getY()); if (polygon.isPrevControlPointUsed(j)) { point = polygon.getPrevControlPoint(j); - SAL_INFO ("drawinglayer", "EMF+\t\t\tPrev: " << point.getX() << "," << point.getY()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tPrev: " << point.getX() << "," << point.getY()); } if (polygon.isNextControlPointUsed(j)) { point = polygon.getNextControlPoint(j); - SAL_INFO ("drawinglayer", "EMF+\t\t\tNext: " << point.getX() << "," << point.getY()); + SAL_INFO ("drawinglayer.emf", "EMF+\t\t\tNext: " << point.getX() << "," << point.getY()); } } } @@ -223,6 +231,91 @@ namespace emfplushelper return aPolygon; } + + static void GetCardinalMatrix(float tension, matrix& m) + { + m[0][1] = 2. - tension; + m[0][2] = tension - 2.; + m[1][0] = 2. * tension; + m[1][1] = tension - 3.; + m[1][2] = 3. - 2. * tension; + m[3][1] = 1.; + m[0][3] = m[2][2] = tension; + m[0][0] = m[1][3] = m[2][0] = -tension; + m[2][1] = m[2][3] = m[3][0] = m[3][2] = m[3][3] = 0.; + } + + static double calculateSplineCoefficients(float p0, float p1, float p2, float p3, sal_uInt32 step, matrix m) + { + double a = m[0][0] * p0 + m[0][1] * p1 + m[0][2] * p2 + m[0][3] * p3; + double b = m[1][0] * p0 + m[1][1] * p1 + m[1][2] * p2 + m[1][3] * p3; + double c = m[2][0] * p0 + m[2][2] * p2; + double d = p1; + return (d + alpha[step] * (c + alpha[step] * (b + alpha[step] * a))); + } + + ::basegfx::B2DPolyPolygon& EMFPPath::GetCardinalSpline(EmfPlusHelperData const& rR, float fTension, + sal_uInt32 aOffset, sal_uInt32 aNumSegments) + { + ::basegfx::B2DPolygon polygon; + matrix mat; + double x, y; + if (aNumSegments >= nPoints) + aNumSegments = nPoints - 1; + GetCardinalMatrix(fTension, mat); + // duplicate first point + xPoints.push_front(xPoints.front()); + yPoints.push_front(yPoints.front()); + // duplicate last point + xPoints.push_back(xPoints.back()); + yPoints.push_back(yPoints.back()); + + for (sal_uInt32 i = 3 + aOffset; i < aNumSegments + 3; i++) + { + for (sal_uInt32 s = 0; s < nDetails; s++) + { + x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1], + xPoints[i], s, mat); + y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1], + yPoints[i], s, mat); + polygon.append(rR.Map(x, y)); + } + } + if (polygon.count()) + aPolygon.append(polygon); + return aPolygon; + } + + ::basegfx::B2DPolyPolygon& EMFPPath::GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension) + { + ::basegfx::B2DPolygon polygon; + matrix mat; + double x, y; + GetCardinalMatrix(fTension, mat); + // add three first points at the end + xPoints.push_back(xPoints[0]); + yPoints.push_back(yPoints[0]); + xPoints.push_back(xPoints[1]); + yPoints.push_back(yPoints[1]); + xPoints.push_back(xPoints[2]); + yPoints.push_back(yPoints[2]); + + for (sal_uInt32 i = 3; i < nPoints + 3; i++) + { + for (sal_uInt32 s = 0; s < nDetails; s++) + { + x = calculateSplineCoefficients(xPoints[i - 3], xPoints[i - 2], xPoints[i - 1], + xPoints[i], s, mat); + y = calculateSplineCoefficients(yPoints[i - 3], yPoints[i - 2], yPoints[i - 1], + yPoints[i], s, mat); + polygon.append(rR.Map(x, y)); + } + } + polygon.setClosed(true); + if (polygon.count()) + aPolygon.append(polygon); + return aPolygon; + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfppath.hxx b/drawinglayer/source/tools/emfppath.hxx index 98996f834dd9..e6104fcb1fc7 100644 --- a/drawinglayer/source/tools/emfppath.hxx +++ b/drawinglayer/source/tools/emfppath.hxx @@ -17,30 +17,31 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPATH_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPATH_HXX +#pragma once #include "emfphelperdata.hxx" namespace emfplushelper { - struct EMFPPath : public EMFPObject + class EMFPPath : public EMFPObject { ::basegfx::B2DPolyPolygon aPolygon; - sal_Int32 nPoints; - std::unique_ptr<float[]> pPoints; + sal_uInt32 nPoints; + std::deque<float> xPoints, yPoints; std::unique_ptr<sal_uInt8[]> pPointTypes; - EMFPPath(sal_Int32 _nPoints, bool bLines = false); + public: + EMFPPath(sal_uInt32 _nPoints, bool bLines = false); virtual ~EMFPPath() override; void Read(SvStream& s, sal_uInt32 pathFlags); ::basegfx::B2DPolyPolygon& GetPolygon(EmfPlusHelperData const & rR, bool bMapIt = true, bool bAddLineToCloseShape = false); + ::basegfx::B2DPolyPolygon& GetCardinalSpline(EmfPlusHelperData const& rR, float fTension, + sal_uInt32 aOffset, sal_uInt32 aNumSegments); + ::basegfx::B2DPolyPolygon& GetClosedCardinalSpline(EmfPlusHelperData const& rR, float fTension); }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx index c5e7d457be61..aaa944660110 100644 --- a/drawinglayer/source/tools/emfppen.cxx +++ b/drawinglayer/source/tools/emfppen.cxx @@ -18,9 +18,9 @@ */ #include <com/sun/star/rendering/PathCapType.hpp> -#include <com/sun/star/rendering/PathJoinType.hpp> #include <o3tl/safeint.hxx> #include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> #include "emfppen.hxx" #include "emfpcustomlinecap.hxx" @@ -30,36 +30,15 @@ using namespace ::basegfx; namespace emfplushelper { - namespace { - - enum EmfPlusPenData - { - PenDataTransform = 0x00000001, - PenDataStartCap = 0x00000002, - PenDataEndCap = 0x00000004, - PenDataJoin = 0x00000008, - PenDataMiterLimit = 0x00000010, - PenDataLineStyle = 0x00000020, - PenDataDashedLineCap = 0x00000040, - PenDataDashedLineOffset = 0x00000080, - PenDataDashedLine = 0x00000100, - PenDataAlignment = 0x00000200, - PenDataCompoundLine = 0x00000400, - PenDataCustomStartCap = 0x00000800, - PenDataCustomEndCap = 0x00001000 - }; - - } EMFPPen::EMFPPen() - : EMFPBrush() - , penDataFlags(0) + : penDataFlags(0) , penUnit(0) , penWidth(0.0) , startCap(0) , endCap(0) - , lineJoin(0) - , miterLimit(0.0) + , maLineJoin(basegfx::B2DLineJoin::Miter) + , fMiterMinimumAngle(basegfx::deg2rad(5.0)) , dashStyle(0) , dashCap(0) , dashOffset(0.0) @@ -75,143 +54,142 @@ namespace emfplushelper static OUString PenDataFlagsToString(sal_uInt32 flags) { - OUString sFlags; + rtl::OUStringBuffer sFlags; if (flags & EmfPlusPenDataTransform) - sFlags = "\nEMF+\t\t\tEmfPlusPenDataTransform"; + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataTransform"); if (flags & EmfPlusPenDataStartCap) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataStartCap"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataStartCap"); if (flags & EmfPlusPenDataEndCap) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataEndCap"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataEndCap"); if (flags & EmfPlusPenDataJoin) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataJoin"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataJoin"); if (flags & EmfPlusPenDataMiterLimit) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataMiterLimit"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataMiterLimit"); if (flags & EmfPlusPenDataLineStyle) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataLineStyle"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataLineStyle"); if (flags & EmfPlusPenDataDashedLineCap) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataDashedLineCap"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineCap"); if (flags & EmfPlusPenDataDashedLineOffset) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataDashedLineOffset"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLineOffset"); if (flags & EmfPlusPenDataDashedLine) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataDashedLine"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataDashedLine"); if (flags & EmfPlusPenDataAlignment) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataAlignment"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataAlignment"); if (flags & EmfPlusPenDataCompoundLine) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataCompoundLine"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCompoundLine"); if (flags & EmfPlusPenDataCustomStartCap) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataCustomStartCap"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomStartCap"); if (flags & EmfPlusPenDataCustomEndCap) - sFlags = sFlags.concat("\nEMF+\t\t\tEmfPlusPenDataCustomEndCap"); + sFlags.append("\nEMF+\t\t\tEmfPlusPenDataCustomEndCap"); - return sFlags; + return sFlags.makeStringAndClear(); } static OUString LineCapTypeToString(sal_uInt32 linecap) { switch (linecap) { - case LineCapTypeFlat: return "LineCapTypeFlat"; - case LineCapTypeSquare: return "LineCapTypeSquare"; - case LineCapTypeRound: return "LineCapTypeRound"; - case LineCapTypeTriangle: return "LineCapTypeTriangle"; - case LineCapTypeNoAnchor: return "LineCapTypeNoAnchor"; - case LineCapTypeSquareAnchor: return "LineCapTypeSquareAnchor"; - case LineCapTypeRoundAnchor: return "LineCapTypeRoundAchor"; - case LineCapTypeDiamondAnchor: return "LineCapTypeDiamondAnchor"; - case LineCapTypeArrowAnchor: return "LineCapTypeArrowAnchor"; - case LineCapTypeAnchorMask: return "LineCapTypeAnchorMask"; - case LineCapTypeCustom: return "LineCapTypeCustom"; - } - return ""; - } - - static OUString LineJoinTypeToString(sal_uInt32 jointype) - { - switch (jointype) - { - case LineJoinTypeMiter: return "LineJoinTypeMiter"; - case LineJoinTypeBevel: return "LineJoinTypeBevel"; - case LineJoinTypeRound: return "LineJoinTypeRound"; - case LineJoinTypeMiterClipped: return "LineJoinTypeMiterClipped"; + case LineCapTypeFlat: return u"LineCapTypeFlat"_ustr; + case LineCapTypeSquare: return u"LineCapTypeSquare"_ustr; + case LineCapTypeRound: return u"LineCapTypeRound"_ustr; + case LineCapTypeTriangle: return u"LineCapTypeTriangle"_ustr; + case LineCapTypeNoAnchor: return u"LineCapTypeNoAnchor"_ustr; + case LineCapTypeSquareAnchor: return u"LineCapTypeSquareAnchor"_ustr; + case LineCapTypeRoundAnchor: return u"LineCapTypeRoundAchor"_ustr; + case LineCapTypeDiamondAnchor: return u"LineCapTypeDiamondAnchor"_ustr; + case LineCapTypeArrowAnchor: return u"LineCapTypeArrowAnchor"_ustr; + case LineCapTypeAnchorMask: return u"LineCapTypeAnchorMask"_ustr; + case LineCapTypeCustom: return u"LineCapTypeCustom"_ustr; } - return ""; + return u""_ustr; } static OUString DashedLineCapTypeToString(sal_uInt32 dashedlinecaptype) { switch (dashedlinecaptype) { - case DashedLineCapTypeFlat: return "DashedLineCapTypeFlat"; - case DashedLineCapTypeRound: return "DashedLineCapTypeRound"; - case DashedLineCapTypeTriangle: return "DashedLineCapTypeTriangle"; + case DashedLineCapTypeFlat: return u"DashedLineCapTypeFlat"_ustr; + case DashedLineCapTypeRound: return u"DashedLineCapTypeRound"_ustr; + case DashedLineCapTypeTriangle: return u"DashedLineCapTypeTriangle"_ustr; } - return ""; + return u""_ustr; } static OUString PenAlignmentToString(sal_uInt32 alignment) { switch (alignment) { - case PenAlignmentCenter: return "PenAlignmentCenter"; - case PenAlignmentInset: return "PenAlignmentInset"; - case PenAlignmentLeft: return "PenAlignmentLeft"; - case PenAlignmentOutset: return "PenAlignmentOutset"; - case PenAlignmentRight: return "PenAlignmentRight"; + case PenAlignmentCenter: return u"PenAlignmentCenter"_ustr; + case PenAlignmentInset: return u"PenAlignmentInset"_ustr; + case PenAlignmentLeft: return u"PenAlignmentLeft"_ustr; + case PenAlignmentOutset: return u"PenAlignmentOutset"_ustr; + case PenAlignmentRight: return u"PenAlignmentRight"_ustr; } - return ""; + return u""_ustr; } - /// Convert stroke caps between EMF+ and rendering API - sal_Int8 EMFPPen::lcl_convertStrokeCap(sal_uInt32 nEmfStroke) + drawinglayer::attribute::StrokeAttribute + EMFPPen::GetStrokeAttribute(const double aTransformation) const { - switch (nEmfStroke) + if (penDataFlags & EmfPlusPenDataLineStyle // pen has a predefined line style + && dashStyle != EmfPlusLineStyleCustom) { - case EmfPlusLineCapTypeSquare: return rendering::PathCapType::SQUARE; - case EmfPlusLineCapTypeRound: return rendering::PathCapType::ROUND; + const double pw = aTransformation * penWidth; + switch (dashStyle) + { + case EmfPlusLineStyleDash: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw }); + case EmfPlusLineStyleDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ pw, pw }); + case EmfPlusLineStyleDashDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw }); + case EmfPlusLineStyleDashDotDot: + // [-loplugin:redundantfcast] false positive: + return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw }); + } } - - // we have no mapping for EmfPlusLineCapTypeTriangle = 0x00000003, - // so return BUTT always - return rendering::PathCapType::BUTT; - } - - sal_Int8 EMFPPen::lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin) - { - switch (nEmfLineJoin) + else if (penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line { - case EmfPlusLineJoinTypeMiter: // fall-through - case EmfPlusLineJoinTypeMiterClipped: return rendering::PathJoinType::MITER; - case EmfPlusLineJoinTypeBevel: return rendering::PathJoinType::BEVEL; - case EmfPlusLineJoinTypeRound: return rendering::PathJoinType::ROUND; + const double pw = aTransformation * penWidth; + // StrokeAttribute needs a double vector while the pen provides a float vector + std::vector<double> aPattern(dashPattern.size()); + for (size_t i = 0; i < aPattern.size(); i++) + { + // convert from float to double and multiply with the adjusted pen width + aPattern[i] = pw * dashPattern[i]; + } + return drawinglayer::attribute::StrokeAttribute(std::move(aPattern)); } - - assert(false); // Line Join type isn't in specification. - return 0; + // EmfPlusLineStyleSolid: - do nothing special, use default stroke attribute + return drawinglayer::attribute::StrokeAttribute(); } void EMFPPen::Read(SvStream& s, EmfPlusHelperData const & rR) { + sal_Int32 lineJoin = EmfPlusLineJoinTypeMiter; sal_uInt32 graphicsVersion, penType; - int i; s.ReadUInt32(graphicsVersion).ReadUInt32(penType).ReadUInt32(penDataFlags).ReadUInt32(penUnit).ReadFloat(penWidth); - SAL_INFO("drawinglayer", "EMF+\t\tGraphics version: 0x" << std::hex << graphicsVersion); - SAL_INFO("drawinglayer", "EMF+\t\tType: " << penType); - SAL_INFO("drawinglayer", "EMF+\t\tPen data flags: 0x" << penDataFlags << PenDataFlagsToString(penDataFlags)); - SAL_INFO("drawinglayer", "EMF+\t\tUnit: " << UnitTypeToString(penUnit)); - SAL_INFO("drawinglayer", "EMF+\t\tWidth: " << std::dec << penWidth); + SAL_INFO("drawinglayer.emf", "EMF+\t\tGraphics version: 0x" << std::hex << graphicsVersion); + SAL_INFO("drawinglayer.emf", "EMF+\t\tType: " << penType); + SAL_INFO("drawinglayer.emf", "EMF+\t\tPen data flags: 0x" << penDataFlags << PenDataFlagsToString(penDataFlags)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tUnit: " << UnitTypeToString(penUnit)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tWidth: " << std::dec << penWidth); // If a zero width is specified, a minimum value must be used, which is determined by the units if (penWidth == 0.0) @@ -220,138 +198,153 @@ namespace emfplushelper : 0.05f; // 0.05f is taken from old EMF+ implementation (case of Unit == Pixel etc.) } - if (penDataFlags & PenDataTransform) + if (penDataFlags & EmfPlusPenDataTransform) { EmfPlusHelperData::readXForm(s, pen_transformation); - SAL_WARN("drawinglayer", "EMF+\t\t TODO PenDataTransform: " << pen_transformation); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataTransform: " << pen_transformation); } - if (penDataFlags & PenDataStartCap) + if (penDataFlags & EmfPlusPenDataStartCap) { s.ReadInt32(startCap); - SAL_INFO("drawinglayer", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap) << " (0x" << std::hex << startCap << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tstartCap: " << LineCapTypeToString(startCap) << " (0x" << std::hex << startCap << ")"); } else { startCap = 0; } - if (penDataFlags & PenDataEndCap) + if (penDataFlags & EmfPlusPenDataEndCap) { s.ReadInt32(endCap); - SAL_INFO("drawinglayer", "EMF+\t\tendCap: " << LineCapTypeToString(endCap) << " (0x" << std::hex << startCap << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tendCap: " << LineCapTypeToString(endCap) << " (0x" << std::hex << startCap << ")"); } else { endCap = 0; } - if (penDataFlags & PenDataJoin) + if (penDataFlags & EmfPlusPenDataJoin) { s.ReadInt32(lineJoin); - SAL_WARN("drawinglayer", "EMF+\t\tTODO PenDataJoin: " << LineJoinTypeToString(lineJoin) << " (0x" << std::hex << lineJoin << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\t\t LineJoin: " << lineJoin); + switch (lineJoin) + { + case EmfPlusLineJoinTypeBevel: + maLineJoin = basegfx::B2DLineJoin::Bevel; + break; + case EmfPlusLineJoinTypeRound: + maLineJoin = basegfx::B2DLineJoin::Round; + break; + case EmfPlusLineJoinTypeMiter: + case EmfPlusLineJoinTypeMiterClipped: + default: // If nothing set, then apply Miter (based on MS Paint) + maLineJoin = basegfx::B2DLineJoin::Miter; + break; + } } else - { - lineJoin = 0; - } + maLineJoin = basegfx::B2DLineJoin::Miter; - if (penDataFlags & PenDataMiterLimit) + if (penDataFlags & EmfPlusPenDataMiterLimit) { + float miterLimit; s.ReadFloat(miterLimit); - SAL_WARN("drawinglayer", "EMF+\t\tTODO PenDataMiterLimit: " << std::dec << miterLimit); + + // EMF+ JoinTypeMiterClipped is working as our B2DLineJoin::Miter + // For EMF+ LineJoinTypeMiter we are simulating it by changing angle + if (lineJoin == EmfPlusLineJoinTypeMiter) + miterLimit = 3.0 * miterLimit; + // asin angle must be in range [-1, 1] + if (abs(miterLimit) > 1.0) + fMiterMinimumAngle = 2.0 * asin(1.0 / miterLimit); + else + // enable miter limit for all angles + fMiterMinimumAngle = basegfx::deg2rad(180.0); + SAL_INFO("drawinglayer.emf", + "EMF+\t\t MiterLimit: " << std::dec << miterLimit + << ", Miter minimum angle (rad): " << fMiterMinimumAngle); } else - { - miterLimit = 0; - } + fMiterMinimumAngle = basegfx::deg2rad(5.0); + - if (penDataFlags & PenDataLineStyle) + if (penDataFlags & EmfPlusPenDataLineStyle) { s.ReadInt32(dashStyle); - SAL_INFO("drawinglayer", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle) << " (0x" << std::hex << dashStyle << ")"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tdashStyle: " << DashedLineCapTypeToString(dashStyle) << " (0x" << std::hex << dashStyle << ")"); } else { dashStyle = 0; } - if (penDataFlags & PenDataDashedLineCap) + if (penDataFlags & EmfPlusPenDataDashedLineCap) { s.ReadInt32(dashCap); - SAL_WARN("drawinglayer", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex << dashCap); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineCap: 0x" << std::hex << dashCap); } else { dashCap = 0; } - if (penDataFlags & PenDataDashedLineOffset) + if (penDataFlags & EmfPlusPenDataDashedLineOffset) { s.ReadFloat(dashOffset); - SAL_WARN("drawinglayer", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex << dashOffset); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO PenDataDashedLineOffset: 0x" << std::hex << dashOffset); } else { dashOffset = 0; } - if (penDataFlags & PenDataDashedLine) + if (penDataFlags & EmfPlusPenDataDashedLine) { dashStyle = EmfPlusLineStyleCustom; - sal_Int32 dashPatternLen; + sal_uInt32 dashPatternLen; - s.ReadInt32(dashPatternLen); - SAL_INFO("drawinglayer", "EMF+\t\t\tdashPatternLen: " << dashPatternLen); - - if (dashPatternLen<0 || o3tl::make_unsigned(dashPatternLen)>SAL_MAX_INT32 / sizeof(float)) - { - dashPatternLen = SAL_MAX_INT32 / sizeof(float); - } + s.ReadUInt32(dashPatternLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tdashPatternLen: " << dashPatternLen); dashPattern.resize( dashPatternLen ); - for (i = 0; i < dashPatternLen; i++) + for (sal_uInt32 i = 0; i < dashPatternLen; i++) { s.ReadFloat(dashPattern[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tdashPattern[" << i << "]: " << dashPattern[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tdashPattern[" << i << "]: " << dashPattern[i]); } } - if (penDataFlags & PenDataAlignment) + if (penDataFlags & EmfPlusPenDataAlignment) { s.ReadInt32(alignment); - SAL_WARN("drawinglayer", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment) << " (0x" << std::hex << alignment << ")"); + SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataAlignment: " << PenAlignmentToString(alignment) << " (0x" << std::hex << alignment << ")"); } else { alignment = 0; } - if (penDataFlags & PenDataCompoundLine) + if (penDataFlags & EmfPlusPenDataCompoundLine) { - SAL_WARN("drawinglayer", "EMF+\t\t\tTODO PenDataCompoundLine"); - sal_Int32 compoundArrayLen; - s.ReadInt32(compoundArrayLen); - - if (compoundArrayLen<0 || o3tl::make_unsigned(compoundArrayLen)>SAL_MAX_INT32 / sizeof(float)) - { - compoundArrayLen = SAL_MAX_INT32 / sizeof(float); - } + SAL_WARN("drawinglayer.emf", "EMF+\t\t\tTODO PenDataCompoundLine"); + sal_uInt32 compoundArrayLen; + s.ReadUInt32(compoundArrayLen); compoundArray.resize(compoundArrayLen); - for (i = 0; i < compoundArrayLen; i++) + for (sal_uInt32 i = 0; i < compoundArrayLen; i++) { s.ReadFloat(compoundArray[i]); - SAL_INFO("drawinglayer", "EMF+\t\t\t\tcompoundArray[" << i << "]: " << compoundArray[i]); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\t\tcompoundArray[" << i << "]: " << compoundArray[i]); } } - if (penDataFlags & PenDataCustomStartCap) + if (penDataFlags & EmfPlusPenDataCustomStartCap) { - s.ReadInt32(customStartCapLen); - SAL_INFO("drawinglayer", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen); + s.ReadUInt32(customStartCapLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomStartCapLen: " << customStartCapLen); sal_uInt64 const pos = s.Tell(); customStartCap.reset( new EMFPCustomLineCap() ); @@ -365,10 +358,10 @@ namespace emfplushelper customStartCapLen = 0; } - if (penDataFlags & PenDataCustomEndCap) + if (penDataFlags & EmfPlusPenDataCustomEndCap) { - s.ReadInt32(customEndCapLen); - SAL_INFO("drawinglayer", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen); + s.ReadUInt32(customEndCapLen); + SAL_INFO("drawinglayer.emf", "EMF+\t\t\tcustomEndCapLen: " << customEndCapLen); sal_uInt64 const pos = s.Tell(); customEndCap.reset( new EMFPCustomLineCap() ); diff --git a/drawinglayer/source/tools/emfppen.hxx b/drawinglayer/source/tools/emfppen.hxx index 72ec12a2bed4..31812c8b0c0e 100644 --- a/drawinglayer/source/tools/emfppen.hxx +++ b/drawinglayer/source/tools/emfppen.hxx @@ -17,9 +17,9 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPEN_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPPEN_HXX +#pragma once +#include <drawinglayer/attribute/strokeattribute.hxx> #include "emfpbrush.hxx" #include <vector> @@ -27,6 +27,7 @@ namespace emfplushelper { const sal_uInt32 EmfPlusLineCapTypeSquare = 0x00000001; const sal_uInt32 EmfPlusLineCapTypeRound = 0x00000002; + const sal_uInt32 EmfPlusLineCapTypeTriangle = 0x00000003; const sal_uInt32 EmfPlusLineJoinTypeMiter = 0x00000000; const sal_uInt32 EmfPlusLineJoinTypeBevel = 0x00000001; @@ -103,17 +104,17 @@ namespace emfplushelper float penWidth; sal_Int32 startCap; sal_Int32 endCap; - sal_Int32 lineJoin; - float miterLimit; + basegfx::B2DLineJoin maLineJoin; + double fMiterMinimumAngle; sal_Int32 dashStyle; sal_Int32 dashCap; float dashOffset; std::vector<float> dashPattern; sal_Int32 alignment; std::vector<float> compoundArray; - sal_Int32 customStartCapLen; + sal_uInt32 customStartCapLen; std::unique_ptr<EMFPCustomLineCap> customStartCap; - sal_Int32 customEndCapLen; + sal_uInt32 customEndCapLen; std::unique_ptr<EMFPCustomLineCap> customEndCap; EMFPPen(); @@ -122,11 +123,8 @@ namespace emfplushelper void Read(SvStream& s, EmfPlusHelperData const & rR); - static sal_Int8 lcl_convertStrokeCap(sal_uInt32 nEmfStroke); - static sal_Int8 lcl_convertLineJoinType(sal_uInt32 nEmfLineJoin); + drawinglayer::attribute::StrokeAttribute GetStrokeAttribute(const double aTransformation) const; }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpregion.cxx b/drawinglayer/source/tools/emfpregion.cxx index 7813e23e24ae..9fcced337733 100644 --- a/drawinglayer/source/tools/emfpregion.cxx +++ b/drawinglayer/source/tools/emfpregion.cxx @@ -46,7 +46,7 @@ namespace emfplushelper sal_uInt32 dataType; ::basegfx::B2DPolyPolygon polygon; s.ReadUInt32(dataType); - SAL_INFO("drawinglayer", "EMF+\t Region node data type 0x" << std::hex << dataType << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t Region node data type 0x" << std::hex << dataType << std::dec); switch (dataType) { @@ -65,32 +65,31 @@ namespace emfplushelper { float dx, dy, dw, dh; s.ReadFloat(dx).ReadFloat(dy).ReadFloat(dw).ReadFloat(dh); - SAL_INFO("drawinglayer", "EMF+\t\t RegionNodeDataTypeRect x:" << dx << ", y:" << dy << + SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeRect x:" << dx << ", y:" << dy << ", width:" << dw << ", height:" << dh); const ::basegfx::B2DPoint mappedStartPoint(rR.Map(dx, dy)); const ::basegfx::B2DPoint mappedEndPoint(rR.Map(dx + dw, dy + dh)); - const ::basegfx::B2DPolyPolygon polyPolygon( + polygon = ::basegfx::B2DPolyPolygon( ::basegfx::utils::createPolygonFromRect( ::basegfx::B2DRectangle( mappedStartPoint.getX(), mappedStartPoint.getY(), mappedEndPoint.getX(), mappedEndPoint.getY()))); - polygon = polyPolygon; break; } case RegionNodeDataTypePath: { sal_Int32 pathLength; s.ReadInt32(pathLength); - SAL_INFO("drawinglayer", "EMF+\t\t RegionNodeDataTypePath, Path Length: " << pathLength << " bytes"); + SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypePath, Path Length: " << pathLength << " bytes"); sal_uInt32 header, pathFlags; sal_Int32 points; s.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags); - SAL_INFO("drawinglayer", "EMF+\t\t header: 0x" << std::hex << header << + SAL_INFO("drawinglayer.emf", "EMF+\t\t header: 0x" << std::hex << header << " points: " << std::dec << points << " additional flags: 0x" << std::hex << pathFlags << std::dec); EMFPPath path(points); @@ -100,21 +99,21 @@ namespace emfplushelper } case RegionNodeDataTypeEmpty: { - SAL_INFO("drawinglayer", "EMF+\t\t RegionNodeDataTypeEmpty"); - SAL_WARN("drawinglayer", "EMF+\t\t TODO we need to set empty polygon here"); + SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeEmpty"); + SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO we need to set empty polygon here"); polygon = ::basegfx::B2DPolyPolygon(); break; } case RegionNodeDataTypeInfinite: { - SAL_INFO("drawinglayer", "EMF+\t\t RegionNodeDataTypeInfinite"); + SAL_INFO("drawinglayer.emf", "EMF+\t\t RegionNodeDataTypeInfinite"); polygon = ::basegfx::B2DPolyPolygon(); break; } default: { - SAL_WARN("drawinglayer", "EMF+\t\t Unhandled region type: 0x" << std::hex << dataType << std::dec); + SAL_WARN("drawinglayer.emf", "EMF+\t\t Unhandled region type: 0x" << std::hex << dataType << std::dec); polygon = ::basegfx::B2DPolyPolygon(); } } @@ -126,7 +125,7 @@ namespace emfplushelper sal_uInt32 header, count; s.ReadUInt32(header).ReadUInt32(count); // An array should be RegionNodeCount+1 of EmfPlusRegionNode objects. - SAL_INFO("drawinglayer", "EMF+\t version: 0x" << std::hex << header << std::dec << ", region node count: " << count); + SAL_INFO("drawinglayer.emf", "EMF+\t version: 0x" << std::hex << header << std::dec << ", region node count: " << count); regionPolyPolygon = ReadRegionNode(s, rR); } diff --git a/drawinglayer/source/tools/emfpregion.hxx b/drawinglayer/source/tools/emfpregion.hxx index a027d9c62b37..a234abbb5539 100644 --- a/drawinglayer/source/tools/emfpregion.hxx +++ b/drawinglayer/source/tools/emfpregion.hxx @@ -17,8 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPREGION_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPREGION_HXX +#pragma once #include "emfphelperdata.hxx" @@ -48,6 +47,4 @@ namespace emfplushelper }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/emfpstringformat.cxx b/drawinglayer/source/tools/emfpstringformat.cxx index ad08f14a21be..c66aa090f438 100644 --- a/drawinglayer/source/tools/emfpstringformat.cxx +++ b/drawinglayer/source/tools/emfpstringformat.cxx @@ -18,6 +18,7 @@ */ #include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> #include "emfpstringformat.hxx" namespace emfplushelper @@ -43,78 +44,42 @@ namespace emfplushelper static OUString StringFormatFlags(sal_uInt32 flag) { - OUString sFlags; + OUStringBuffer sFlags; + // These are extracted from enum in emfpstringformat.hxx if (flag & StringFormatDirectionRightToLeft) - sFlags = sFlags.concat("StringFormatDirectionRightToLeft"); + sFlags.append("StringFormatDirectionRightToLeft "); - if (flag & StringFormatDirectionRightToLeft) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); - - sFlags = sFlags.concat("StringFormatDirectionRightToLeft"); - } + if (flag & StringFormatDirectionVertical) + sFlags.append("StringFormatDirectionVertical "); if (flag & StringFormatNoFitBlackBox) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); - - sFlags = sFlags.concat("StringFormatNoFitBlackBox"); - } + sFlags.append("StringFormatNoFitBlackBox "); if (flag & StringFormatDisplayFormatControl) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatDisplayFormatControl "); - sFlags = sFlags.concat("StringFormatDisplayFormatControl"); - } if (flag & StringFormatNoFontFallback) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatNoFontFallback "); - sFlags = sFlags.concat("StringFormatNoFontFallback"); - } if (flag & StringFormatMeasureTrailingSpaces) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatMeasureTrailingSpaces "); - sFlags = sFlags.concat("StringFormatMeasureTrailingSpaces"); - } if (flag & StringFormatNoWrap) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatNoWrap "); - sFlags = sFlags.concat("StringFormatNoWrap"); - } if (flag & StringFormatLineLimit) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatLineLimit "); - sFlags = sFlags.concat("StringFormatLineLimit"); - } if (flag & StringFormatNoClip) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatNoClip "); - sFlags = sFlags.concat("StringFormatNoClip"); - } if (flag & StringFormatBypassGDI) - { - if (!sFlags.isEmpty()) - sFlags = sFlags.concat(", "); + sFlags.append("StringFormatBypassGDI "); - sFlags = sFlags.concat("StringFormatBypassGDI"); - } - - return sFlags; + // There will be 1 extra space in the end. It could be truncated, but + // as it is for SAL_INFO() only, it would not be important + return sFlags.makeStringAndClear(); } static OUString StringAlignmentString(sal_uInt32 nAlignment) @@ -122,14 +87,14 @@ namespace emfplushelper switch(nAlignment) { case StringAlignment::StringAlignmentNear: - return "StringAlignmentNear"; + return u"StringAlignmentNear"_ustr; case StringAlignment::StringAlignmentCenter: - return "StringAlignmentCenter"; + return u"StringAlignmentCenter"_ustr; case StringAlignment::StringAlignmentFar: - return "StringAlignmentFar"; + return u"StringAlignmentFar"_ustr; default: assert(false && nAlignment && "invalid string alignment value"); - return "INVALID"; + return u"INVALID"_ustr; } } @@ -138,16 +103,16 @@ namespace emfplushelper switch(nSubst) { case StringDigitSubstitution::StringDigitSubstitutionUser: - return "StringDigitSubstitutionUser"; + return u"StringDigitSubstitutionUser"_ustr; case StringDigitSubstitution::StringDigitSubstitutionNone: - return "StringDigitSubstitutionNone"; + return u"StringDigitSubstitutionNone"_ustr; case StringDigitSubstitution::StringDigitSubstitutionNational: - return "StringDigitSubstitutionNational"; + return u"StringDigitSubstitutionNational"_ustr; case StringDigitSubstitution::StringDigitSubstitutionTraditional: - return "StringDigitSubstitutionTraditional"; + return u"StringDigitSubstitutionTraditional"_ustr; default: assert(false && nSubst && "invalid string digit substitution value"); - return "INVALID"; + return u"INVALID"_ustr; } } @@ -156,14 +121,14 @@ namespace emfplushelper switch(nHotkey) { case HotkeyPrefix::HotkeyPrefixNone: - return "HotkeyPrefixNone"; + return u"HotkeyPrefixNone"_ustr; case HotkeyPrefix::HotkeyPrefixShow: - return "HotkeyPrefixShow"; + return u"HotkeyPrefixShow"_ustr; case HotkeyPrefix::HotkeyPrefixHide: - return "HotkeyPrefixHide"; + return u"HotkeyPrefixHide"_ustr; default: assert(false && nHotkey && "invalid hotkey prefix value"); - return "INVALID"; + return u"INVALID"_ustr; } } @@ -172,20 +137,20 @@ namespace emfplushelper switch(nTrimming) { case StringTrimming::StringTrimmingNone: - return "StringTrimmingNone"; + return u"StringTrimmingNone"_ustr; case StringTrimming::StringTrimmingCharacter: - return "StringTrimmingCharacter"; + return u"StringTrimmingCharacter"_ustr; case StringTrimming::StringTrimmingWord: - return "StringTrimmingWord"; + return u"StringTrimmingWord"_ustr; case StringTrimming::StringTrimmingEllipsisCharacter: - return "StringTrimmingEllipsisCharacter"; + return u"StringTrimmingEllipsisCharacter"_ustr; case StringTrimming::StringTrimmingEllipsisWord: - return "StringTrimmingEllipsisWord"; + return u"StringTrimmingEllipsisWord"_ustr; case StringTrimming::StringTrimmingEllipsisPath: - return "StringTrimmingEllipsisPath"; + return u"StringTrimmingEllipsisPath"_ustr; default: assert(false && nTrimming && "invalid trim value"); - return "INVALID"; + return u"INVALID"_ustr; } } @@ -198,36 +163,32 @@ namespace emfplushelper // keep only the last 16 bits of language language >>= 16; digitLanguage >>= 16; - SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer", "Invalid header - not 0xdbc01"); - SAL_INFO("drawinglayer", "EMF+\tString format"); - SAL_INFO("drawinglayer", "EMF+\t\tHeader: 0x" << std::hex << (header >> 12)); - SAL_INFO("drawinglayer", "EMF+\t\tVersion: 0x" << (header & 0x1fff) << std::dec); - SAL_INFO("drawinglayer", "EMF+\t\tStringFormatFlags: " << StringFormatFlags(stringFormatFlags)); - SAL_INFO("drawinglayer", "EMF+\t\tLanguage: sublangid: 0x" << std::hex << (language >> 10) << ", primarylangid: 0x" << (language & 0xF800)); - SAL_INFO("drawinglayer", "EMF+\t\tLineAlign: " << StringAlignmentString(lineAlign)); - SAL_INFO("drawinglayer", "EMF+\t\tDigitSubstitution: " << DigitSubstitutionString(digitSubstitution)); - SAL_INFO("drawinglayer", "EMF+\t\tDigitLanguage: sublangid: 0x" << std::hex << (digitLanguage >> 10) << ", primarylangid: 0x" << (digitLanguage & 0xF800)); - SAL_INFO("drawinglayer", "EMF+\t\tFirstTabOffset: " << firstTabOffset); - SAL_INFO("drawinglayer", "EMF+\t\tHotkeyPrefix: " << HotkeyPrefixString(hotkeyPrefix)); - SAL_INFO("drawinglayer", "EMF+\t\tLeadingMargin: " << leadingMargin); - SAL_INFO("drawinglayer", "EMF+\t\tTrailingMargin: " << trailingMargin); - SAL_INFO("drawinglayer", "EMF+\t\tTracking: " << tracking); - SAL_INFO("drawinglayer", "EMF+\t\tTrimming: " << StringTrimmingString(trimming)); - SAL_INFO("drawinglayer", "EMF+\t\tTabStopCount: " << tabStopCount); - SAL_INFO("drawinglayer", "EMF+\t\tRangeCount: " << rangeCount); - - SAL_WARN_IF(stringAlignment != StringAlignment::StringAlignmentNear, "drawinglayer", "EMF+\t TODO EMFPStringFormat:StringAlignment"); - SAL_WARN_IF(lineAlign != StringAlignment::StringAlignmentNear, "drawinglayer", "EMF+\t TODO EMFPStringFormat:lineAlign"); + SAL_WARN_IF((header >> 12) != 0xdbc01, "drawinglayer.emf", "Invalid header - not 0xdbc01"); + SAL_INFO("drawinglayer.emf", "EMF+\tString format"); + SAL_INFO("drawinglayer.emf", "EMF+\t\tHeader: 0x" << std::hex << (header >> 12)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << (header & 0x1fff) << std::dec); + SAL_INFO("drawinglayer.emf", "EMF+\t\tStringFormatFlags: " << StringFormatFlags(stringFormatFlags)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tLanguage: sublangid: 0x" << std::hex << (language >> 10) << ", primarylangid: 0x" << (language & 0xF800)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tLineAlign: " << StringAlignmentString(lineAlign)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tDigitSubstitution: " << DigitSubstitutionString(digitSubstitution)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tDigitLanguage: sublangid: 0x" << std::hex << (digitLanguage >> 10) << ", primarylangid: 0x" << (digitLanguage & 0xF800)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tFirstTabOffset: " << firstTabOffset); + SAL_INFO("drawinglayer.emf", "EMF+\t\tHotkeyPrefix: " << HotkeyPrefixString(hotkeyPrefix)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tLeadingMargin: " << leadingMargin); + SAL_INFO("drawinglayer.emf", "EMF+\t\tTrailingMargin: " << trailingMargin); + SAL_INFO("drawinglayer.emf", "EMF+\t\tTracking: " << tracking); + SAL_INFO("drawinglayer.emf", "EMF+\t\tTrimming: " << StringTrimmingString(trimming)); + SAL_INFO("drawinglayer.emf", "EMF+\t\tTabStopCount: " << tabStopCount); + SAL_INFO("drawinglayer.emf", "EMF+\t\tRangeCount: " << rangeCount); + SAL_WARN_IF(digitSubstitution != StringDigitSubstitution::StringDigitSubstitutionNone, - "drawinglayer", "EMF+\t TODO EMFPStringFormat:digitSubstitution"); - SAL_WARN_IF(firstTabOffset != 0.0, "drawinglayer", "EMF+\t TODO EMFPStringFormat:firstTabOffset"); - SAL_WARN_IF(hotkeyPrefix != HotkeyPrefix::HotkeyPrefixNone, "drawinglayer", "EMF+\t TODO EMFPStringFormat:hotkeyPrefix"); - SAL_WARN_IF(leadingMargin != 0.0, "drawinglayer", "EMF+\t TODO EMFPStringFormat:leadingMargin"); - SAL_WARN_IF(trailingMargin != 0.0, "drawinglayer", "EMF+\t TODO EMFPStringFormat:trailingMargin"); - SAL_WARN_IF(tracking != 1.0, "drawinglayer", "EMF+\t TODO EMFPStringFormat:tracking"); - SAL_WARN_IF(trimming != StringTrimming::StringTrimmingNone, "drawinglayer", "EMF+\t TODO EMFPStringFormat:trimming"); - SAL_WARN_IF(tabStopCount, "drawinglayer", "EMF+\t TODO EMFPStringFormat:tabStopCount"); - SAL_WARN_IF(rangeCount, "drawinglayer", "EMF+\t TODO EMFPStringFormat:StringFormatData"); + "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:digitSubstitution"); + SAL_WARN_IF(firstTabOffset != 0.0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:firstTabOffset"); + SAL_WARN_IF(hotkeyPrefix != HotkeyPrefix::HotkeyPrefixNone, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:hotkeyPrefix"); + SAL_WARN_IF(tracking != 1.0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:tracking"); + SAL_WARN_IF(trimming != StringTrimming::StringTrimmingNone, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:trimming"); + SAL_WARN_IF(tabStopCount, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:tabStopCount"); + SAL_WARN_IF(rangeCount != 0, "drawinglayer.emf", "EMF+\t TODO EMFPStringFormat:StringFormatData"); } } diff --git a/drawinglayer/source/tools/emfpstringformat.hxx b/drawinglayer/source/tools/emfpstringformat.hxx index 69cad15a144d..b4d8cb380e36 100644 --- a/drawinglayer/source/tools/emfpstringformat.hxx +++ b/drawinglayer/source/tools/emfpstringformat.hxx @@ -17,8 +17,7 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#ifndef INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPSTRINGFORMAT_HXX -#define INCLUDED_DRAWINGLAYER_SOURCE_TOOLS_EMFPSTRINGFORMAT_HXX +#pragma once #include "emfphelperdata.hxx" @@ -102,6 +101,4 @@ namespace emfplushelper }; } -#endif - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/source/tools/primitive2dxmldump.cxx b/drawinglayer/source/tools/primitive2dxmldump.cxx index 7e68a780a84c..3cffb08bf81c 100644 --- a/drawinglayer/source/tools/primitive2dxmldump.cxx +++ b/drawinglayer/source/tools/primitive2dxmldump.cxx @@ -13,43 +13,65 @@ #include <tools/stream.hxx> #include <tools/XmlWriter.hxx> +#include <math.h> #include <memory> +#include <libxml/parser.h> #include <sal/log.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> #include <drawinglayer/primitive2d/Tools.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> +#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <primitive2d/textlineprimitive2d.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <drawinglayer/primitive2d/maskprimitive2d.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> +#include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonAlphaGradientPrimitive2D.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <drawinglayer/attribute/lineattribute.hxx> #include <drawinglayer/attribute/fontattribute.hxx> #include <basegfx/polygon/b2dpolypolygontools.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <drawinglayer/primitive3d/baseprimitive3d.hxx> +#include <drawinglayer/primitive3d/Tools.hxx> +#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx> +#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx> +#include <drawinglayer/attribute/sdrlightattribute3d.hxx> +#include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <drawinglayer/attribute/fillhatchattribute.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <drawinglayer/attribute/materialattribute3d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> using namespace drawinglayer::primitive2d; namespace drawinglayer { - namespace { - const size_t constMaxActionType = 513; OUString convertColorToString(const basegfx::BColor& rColor) @@ -58,6 +80,39 @@ OUString convertColorToString(const basegfx::BColor& rColor) return "#" + aRGBString; } +void writeMatrix(::tools::XmlWriter& rWriter, const basegfx::B2DHomMatrix& rMatrix) +{ + rWriter.attribute("xy11", rMatrix.get(0, 0)); + rWriter.attribute("xy12", rMatrix.get(0, 1)); + rWriter.attribute("xy13", rMatrix.get(0, 2)); + rWriter.attribute("xy21", rMatrix.get(1, 0)); + rWriter.attribute("xy22", rMatrix.get(1, 1)); + rWriter.attribute("xy23", rMatrix.get(1, 2)); + rWriter.attribute("xy31", 0); + rWriter.attribute("xy32", 0); + rWriter.attribute("xy33", 1); +} + +void writeMatrix3D(::tools::XmlWriter& rWriter, const basegfx::B3DHomMatrix& rMatrix) +{ + rWriter.attribute("xy11", rMatrix.get(0, 0)); + rWriter.attribute("xy12", rMatrix.get(0, 1)); + rWriter.attribute("xy13", rMatrix.get(0, 2)); + rWriter.attribute("xy14", rMatrix.get(0, 3)); + rWriter.attribute("xy21", rMatrix.get(1, 0)); + rWriter.attribute("xy22", rMatrix.get(1, 1)); + rWriter.attribute("xy23", rMatrix.get(1, 2)); + rWriter.attribute("xy24", rMatrix.get(1, 3)); + rWriter.attribute("xy31", rMatrix.get(2, 0)); + rWriter.attribute("xy32", rMatrix.get(2, 1)); + rWriter.attribute("xy33", rMatrix.get(2, 2)); + rWriter.attribute("xy34", rMatrix.get(2, 3)); + rWriter.attribute("xy41", rMatrix.get(3, 0)); + rWriter.attribute("xy42", rMatrix.get(3, 1)); + rWriter.attribute("xy43", rMatrix.get(3, 2)); + rWriter.attribute("xy44", rMatrix.get(3, 3)); +} + void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon& rB2DPolyPolygon) { rWriter.startElement("polypolygon"); @@ -70,12 +125,12 @@ void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon rWriter.attributeDouble("maxy", aB2DRange.getMaxY()); rWriter.attribute("path", basegfx::utils::exportToSvgD(rB2DPolyPolygon, true, true, false)); - for (basegfx::B2DPolygon const & rPolygon : rB2DPolyPolygon) + for (basegfx::B2DPolygon const& rPolygon : rB2DPolyPolygon) { rWriter.startElement("polygon"); - for (sal_uInt32 i = 0; i <rPolygon.count(); ++i) + for (sal_uInt32 i = 0; i < rPolygon.count(); ++i) { - basegfx::B2DPoint const & rPoint = rPolygon.getB2DPoint(i); + basegfx::B2DPoint const& rPoint = rPolygon.getB2DPoint(i); rWriter.startElement("point"); rWriter.attribute("x", OUString::number(rPoint.getX())); @@ -88,11 +143,377 @@ void writePolyPolygon(::tools::XmlWriter& rWriter, const basegfx::B2DPolyPolygon rWriter.endElement(); } +void writeStrokeAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute) +{ + if (!rStrokeAttribute.getDotDashArray().empty()) + { + rWriter.startElement("stroke"); + + OUString sDotDash; + for (double fDotDash : rStrokeAttribute.getDotDashArray()) + { + sDotDash += OUString::number(lround(fDotDash)) + " "; + } + rWriter.attribute("dotDashArray", sDotDash); + rWriter.attribute("fullDotDashLength", rStrokeAttribute.getFullDotDashLen()); + rWriter.endElement(); + } +} + +void writeLineAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::LineAttribute& rLineAttribute) +{ + rWriter.startElement("line"); + rWriter.attribute("color", convertColorToString(rLineAttribute.getColor())); + rWriter.attribute("width", rLineAttribute.getWidth()); + switch (rLineAttribute.getLineJoin()) + { + case basegfx::B2DLineJoin::NONE: + rWriter.attribute("linejoin", "NONE"_ostr); + break; + case basegfx::B2DLineJoin::Bevel: + rWriter.attribute("linejoin", "Bevel"_ostr); + break; + case basegfx::B2DLineJoin::Miter: + { + rWriter.attribute("linejoin", "Miter"_ostr); + rWriter.attribute("miterangle", + basegfx::rad2deg(rLineAttribute.getMiterMinimumAngle())); + break; + } + case basegfx::B2DLineJoin::Round: + rWriter.attribute("linejoin", "Round"_ostr); + break; + default: + rWriter.attribute("linejoin", "Unknown"_ostr); + break; + } + switch (rLineAttribute.getLineCap()) + { + case css::drawing::LineCap::LineCap_BUTT: + rWriter.attribute("linecap", "BUTT"_ostr); + break; + case css::drawing::LineCap::LineCap_ROUND: + rWriter.attribute("linecap", "ROUND"_ostr); + break; + case css::drawing::LineCap::LineCap_SQUARE: + rWriter.attribute("linecap", "SQUARE"_ostr); + break; + default: + rWriter.attribute("linecap", "Unknown"_ostr); + break; + } + + rWriter.endElement(); +} + +void writeSdrLineAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::SdrLineAttribute& rLineAttribute) +{ + if (rLineAttribute.isDefault()) + return; + + rWriter.startElement("line"); + rWriter.attribute("color", convertColorToString(rLineAttribute.getColor())); + rWriter.attribute("width", rLineAttribute.getWidth()); + rWriter.attribute("transparence", rLineAttribute.getTransparence()); + + switch (rLineAttribute.getJoin()) + { + case basegfx::B2DLineJoin::NONE: + rWriter.attribute("linejoin", "NONE"_ostr); + break; + case basegfx::B2DLineJoin::Bevel: + rWriter.attribute("linejoin", "Bevel"_ostr); + break; + case basegfx::B2DLineJoin::Miter: + rWriter.attribute("linejoin", "Miter"_ostr); + break; + case basegfx::B2DLineJoin::Round: + rWriter.attribute("linejoin", "Round"_ostr); + break; + default: + rWriter.attribute("linejoin", "Unknown"_ostr); + break; + } + switch (rLineAttribute.getCap()) + { + case css::drawing::LineCap::LineCap_BUTT: + rWriter.attribute("linecap", "BUTT"_ostr); + break; + case css::drawing::LineCap::LineCap_ROUND: + rWriter.attribute("linecap", "ROUND"_ostr); + break; + case css::drawing::LineCap::LineCap_SQUARE: + rWriter.attribute("linecap", "SQUARE"_ostr); + break; + default: + rWriter.attribute("linecap", "Unknown"_ostr); + break; + } + + if (!rLineAttribute.getDotDashArray().empty()) + { + OUString sDotDash; + for (double fDotDash : rLineAttribute.getDotDashArray()) + { + sDotDash += OUString::number(fDotDash) + " "; + } + rWriter.attribute("dotDashArray", sDotDash); + rWriter.attribute("fullDotDashLength", rLineAttribute.getFullDotDashLen()); + } + + rWriter.endElement(); +} + +void writeSdrFillAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::SdrFillAttribute& rFillAttribute) +{ + if (rFillAttribute.isDefault()) + return; + + rWriter.startElement("fill"); + rWriter.attribute("color", convertColorToString(rFillAttribute.getColor())); + rWriter.attribute("transparence", rFillAttribute.getTransparence()); + + auto const& rGradient = rFillAttribute.getGradient(); + if (!rGradient.isDefault()) + { + rWriter.startElement("gradient"); + switch (rGradient.getStyle()) + { + default: // GradientStyle_MAKE_FIXED_SIZE + case css::awt::GradientStyle_LINEAR: + rWriter.attribute("style", "Linear"_ostr); + break; + case css::awt::GradientStyle_AXIAL: + rWriter.attribute("style", "Axial"_ostr); + break; + case css::awt::GradientStyle_RADIAL: + rWriter.attribute("style", "Radial"_ostr); + break; + case css::awt::GradientStyle_ELLIPTICAL: + rWriter.attribute("style", "Elliptical"_ostr); + break; + case css::awt::GradientStyle_SQUARE: + rWriter.attribute("style", "Square"_ostr); + break; + case css::awt::GradientStyle_RECT: + rWriter.attribute("style", "Rect"_ostr); + break; + } + rWriter.attribute("border", rGradient.getBorder()); + rWriter.attribute("offsetX", rGradient.getOffsetX()); + rWriter.attribute("offsetY", rGradient.getOffsetY()); + rWriter.attribute("angle", rGradient.getAngle()); + rWriter.attribute("steps", rGradient.getSteps()); + + auto const& rColorStops(rGradient.getColorStops()); + for (size_t a(0); a < rColorStops.size(); a++) + { + if (0 == a) + rWriter.attribute("startColor", + convertColorToString(rColorStops[a].getStopColor())); + else if (rColorStops.size() == a + 1) + rWriter.attribute("endColor", convertColorToString(rColorStops[a].getStopColor())); + else + { + rWriter.startElement("colorStop"); + rWriter.attribute("stopOffset", rColorStops[a].getStopOffset()); + rWriter.attribute("stopColor", convertColorToString(rColorStops[a].getStopColor())); + rWriter.endElement(); + } + } + rWriter.endElement(); + } + + auto const& rHatch = rFillAttribute.getHatch(); + if (!rHatch.isDefault()) + { + rWriter.startElement("hatch"); + switch (rHatch.getStyle()) + { + case drawinglayer::attribute::HatchStyle::Single: + rWriter.attribute("style", "Single"_ostr); + break; + case drawinglayer::attribute::HatchStyle::Double: + rWriter.attribute("style", "Double"_ostr); + break; + case drawinglayer::attribute::HatchStyle::Triple: + rWriter.attribute("style", "Triple"_ostr); + break; + } + rWriter.attribute("distance", rHatch.getDistance()); + rWriter.attribute("angle", rHatch.getAngle()); + rWriter.attribute("color", convertColorToString(rHatch.getColor())); + rWriter.attribute("minimalDescreteDistance", rHatch.getMinimalDiscreteDistance()); + rWriter.attribute("isFillBackground", sal_Int32(rHatch.isFillBackground())); + rWriter.endElement(); + } + + auto const& rGraphic = rFillAttribute.getFillGraphic(); + if (!rGraphic.isDefault()) + { + rWriter.startElement("graphic"); + // TODO + rWriter.endElement(); + } + + rWriter.endElement(); +} + +void writeShadeMode(::tools::XmlWriter& rWriter, const css::drawing::ShadeMode& rMode) +{ + switch (rMode) + { + case css::drawing::ShadeMode_FLAT: + rWriter.attribute("shadeMode", "Flat"_ostr); + break; + case css::drawing::ShadeMode_SMOOTH: + rWriter.attribute("shadeMode", "Smooth"_ostr); + break; + case css::drawing::ShadeMode_PHONG: + rWriter.attribute("shadeMode", "Phong"_ostr); + break; + case css::drawing::ShadeMode_DRAFT: + rWriter.attribute("shadeMode", "Draft"_ostr); + break; + default: + rWriter.attribute("shadeMode", "Undefined"_ostr); + break; + } +} + +void writeProjectionMode(::tools::XmlWriter& rWriter, const css::drawing::ProjectionMode& rMode) +{ + switch (rMode) + { + case css::drawing::ProjectionMode_PARALLEL: + rWriter.attribute("projectionMode", "Parallel"_ostr); + break; + case css::drawing::ProjectionMode_PERSPECTIVE: + rWriter.attribute("projectionMode", "Perspective"_ostr); + break; + default: + rWriter.attribute("projectionMode", "Undefined"_ostr); + break; + } +} + +void writeNormalsKind(::tools::XmlWriter& rWriter, const css::drawing::NormalsKind& rKind) +{ + switch (rKind) + { + case css::drawing::NormalsKind_SPECIFIC: + rWriter.attribute("normalsKind", "Specific"_ostr); + break; + case css::drawing::NormalsKind_FLAT: + rWriter.attribute("normalsKind", "Flat"_ostr); + break; + case css::drawing::NormalsKind_SPHERE: + rWriter.attribute("normalsKind", "Sphere"_ostr); + break; + default: + rWriter.attribute("normalsKind", "Undefined"_ostr); + break; + } +} + +void writeTextureProjectionMode(::tools::XmlWriter& rWriter, const char* pElement, + const css::drawing::TextureProjectionMode& rMode) +{ + switch (rMode) + { + case css::drawing::TextureProjectionMode_OBJECTSPECIFIC: + rWriter.attribute(pElement, "Specific"_ostr); + break; + case css::drawing::TextureProjectionMode_PARALLEL: + rWriter.attribute(pElement, "Parallel"_ostr); + break; + case css::drawing::TextureProjectionMode_SPHERE: + rWriter.attribute(pElement, "Sphere"_ostr); + break; + default: + rWriter.attribute(pElement, "Undefined"_ostr); + break; + } +} + +void writeTextureKind(::tools::XmlWriter& rWriter, const css::drawing::TextureKind2& rKind) +{ + switch (rKind) + { + case css::drawing::TextureKind2_LUMINANCE: + rWriter.attribute("textureKind", "Luminance"_ostr); + break; + case css::drawing::TextureKind2_INTENSITY: + rWriter.attribute("textureKind", "Intensity"_ostr); + break; + case css::drawing::TextureKind2_COLOR: + rWriter.attribute("textureKind", "Color"_ostr); + break; + default: + rWriter.attribute("textureKind", "Undefined"_ostr); + break; + } +} + +void writeTextureMode(::tools::XmlWriter& rWriter, const css::drawing::TextureMode& rMode) +{ + switch (rMode) + { + case css::drawing::TextureMode_REPLACE: + rWriter.attribute("textureMode", "Replace"_ostr); + break; + case css::drawing::TextureMode_MODULATE: + rWriter.attribute("textureMode", "Modulate"_ostr); + break; + case css::drawing::TextureMode_BLEND: + rWriter.attribute("textureMode", "Blend"_ostr); + break; + default: + rWriter.attribute("textureMode", "Undefined"_ostr); + break; + } +} + +void writeMaterialAttribute(::tools::XmlWriter& rWriter, + const drawinglayer::attribute::MaterialAttribute3D& rMaterial) +{ + rWriter.startElement("material"); + rWriter.attribute("color", convertColorToString(rMaterial.getColor())); + rWriter.attribute("specular", convertColorToString(rMaterial.getSpecular())); + rWriter.attribute("emission", convertColorToString(rMaterial.getEmission())); + rWriter.attribute("specularIntensity", rMaterial.getSpecularIntensity()); + rWriter.endElement(); +} + +void writeSpreadMethod(::tools::XmlWriter& rWriter, + const drawinglayer::primitive2d::SpreadMethod& rSpreadMethod) +{ + switch (rSpreadMethod) + { + case drawinglayer::primitive2d::SpreadMethod::Pad: + rWriter.attribute("spreadmethod", "pad"_ostr); + break; + case drawinglayer::primitive2d::SpreadMethod::Reflect: + rWriter.attribute("spreadmethod", "reflect"_ostr); + break; + case drawinglayer::primitive2d::SpreadMethod::Repeat: + rWriter.attribute("spreadmethod", "repeat"_ostr); + break; + default: + rWriter.attribute("spreadmethod", "unknown"_ostr); + } +} + } // end anonymous namespace -Primitive2dXmlDump::Primitive2dXmlDump() : - maFilter(constMaxActionType, false) -{} +Primitive2dXmlDump::Primitive2dXmlDump() + : maFilter(constMaxActionType, false) +{ +} Primitive2dXmlDump::~Primitive2dXmlDump() = default; @@ -119,6 +540,101 @@ void Primitive2dXmlDump::dump( pStream->Seek(STREAM_SEEK_TO_BEGIN); } +namespace +{ +class Primitive3DXmlDump +{ +public: + void decomposeAndWrite(const drawinglayer::primitive3d::Primitive3DContainer& rSequence, + ::tools::XmlWriter& rWriter) + { + for (size_t i = 0; i < rSequence.size(); i++) + { + const drawinglayer::primitive3d::Primitive3DReference& xReference = rSequence[i]; + const auto* pBasePrimitive + = static_cast<const drawinglayer::primitive3d::BasePrimitive3D*>(xReference.get()); + sal_uInt32 nId = pBasePrimitive->getPrimitive3DID(); + OUString sCurrentElementTag = drawinglayer::primitive3d::idToString(nId); + switch (nId) + { + case PRIMITIVE3D_ID_SDREXTRUDEPRIMITIVE3D: + { + const auto* pExtrudePrimitive3D + = static_cast<const drawinglayer::primitive3d::SdrExtrudePrimitive3D*>( + xReference.get()); + rWriter.startElement("extrude3D"); + + rWriter.startElement("matrix3D"); + writeMatrix3D(rWriter, pExtrudePrimitive3D->getTransform()); + rWriter.endElement(); + + rWriter.attribute("textureSizeX", pExtrudePrimitive3D->getTextureSize().getX()); + rWriter.attribute("textureSizeY", pExtrudePrimitive3D->getTextureSize().getY()); + auto const& rLFSAttribute = pExtrudePrimitive3D->getSdrLFSAttribute(); + writeSdrLineAttribute(rWriter, rLFSAttribute.getLine()); + writeSdrFillAttribute(rWriter, rLFSAttribute.getFill()); + + rWriter.startElement("object3Dattributes"); + { + auto const& r3DObjectAttributes + = pExtrudePrimitive3D->getSdr3DObjectAttribute(); + + writeNormalsKind(rWriter, r3DObjectAttributes.getNormalsKind()); + writeTextureProjectionMode(rWriter, "textureProjectionX", + r3DObjectAttributes.getTextureProjectionX()); + writeTextureProjectionMode(rWriter, "textureProjectionY", + r3DObjectAttributes.getTextureProjectionY()); + writeTextureKind(rWriter, r3DObjectAttributes.getTextureKind()); + writeTextureMode(rWriter, r3DObjectAttributes.getTextureMode()); + writeMaterialAttribute(rWriter, r3DObjectAttributes.getMaterial()); + + rWriter.attribute("normalsInvert", + sal_Int32(r3DObjectAttributes.getNormalsInvert())); + rWriter.attribute("doubleSided", + sal_Int32(r3DObjectAttributes.getDoubleSided())); + rWriter.attribute("shadow3D", sal_Int32(r3DObjectAttributes.getShadow3D())); + rWriter.attribute("textureFilter", + sal_Int32(r3DObjectAttributes.getTextureFilter())); + rWriter.attribute("reducedGeometry", + sal_Int32(r3DObjectAttributes.getReducedLineGeometry())); + } + rWriter.endElement(); + + rWriter.attribute("depth", pExtrudePrimitive3D->getDepth()); + rWriter.attribute("diagonal", pExtrudePrimitive3D->getDiagonal()); + rWriter.attribute("backScale", pExtrudePrimitive3D->getBackScale()); + rWriter.attribute("smoothNormals", + sal_Int32(pExtrudePrimitive3D->getSmoothNormals())); + rWriter.attribute("smoothLids", + sal_Int32(pExtrudePrimitive3D->getSmoothLids())); + rWriter.attribute("characterMode", + sal_Int32(pExtrudePrimitive3D->getCharacterMode())); + rWriter.attribute("closeFront", + sal_Int32(pExtrudePrimitive3D->getCloseFront())); + rWriter.attribute("closeBack", sal_Int32(pExtrudePrimitive3D->getCloseBack())); + writePolyPolygon(rWriter, pExtrudePrimitive3D->getPolyPolygon()); + rWriter.endElement(); + } + break; + + default: + { + rWriter.startElement("unhandled"); + rWriter.attribute("id", sCurrentElementTag); + rWriter.attribute("idNumber", nId); + + drawinglayer::geometry::ViewInformation3D aViewInformation3D; + drawinglayer::primitive3d::Primitive3DContainer aContainer; + aContainer = pBasePrimitive->get3DDecomposition(aViewInformation3D); + decomposeAndWrite(aContainer, rWriter); + rWriter.endElement(); + } + break; + } + } + } +}; +} xmlDocUniquePtr Primitive2dXmlDump::dumpAndParse( const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence, const OUString& rStreamName) @@ -150,27 +666,73 @@ xmlDocUniquePtr Primitive2dXmlDump::dumpAndParse( return xmlDocUniquePtr(xmlParseDoc(reinterpret_cast<xmlChar*>(pBuffer.get()))); } +OUString Primitive2dXmlDump::idToString(sal_uInt32 nId) +{ + return drawinglayer::primitive2d::idToString(nId); +} + +void Primitive2dXmlDump::runDecomposeAndRecurse(const BasePrimitive2D* pBasePrimitive, + ::tools::XmlWriter& rWriter) +{ + drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; + pBasePrimitive->get2DDecomposition(aPrimitiveContainer, + drawinglayer::geometry::ViewInformation2D()); + decomposeAndWrite(aPrimitiveContainer, rWriter); +} + void Primitive2dXmlDump::decomposeAndWrite( const drawinglayer::primitive2d::Primitive2DContainer& rPrimitive2DSequence, ::tools::XmlWriter& rWriter) { - for (size_t i = 0; i < rPrimitive2DSequence.size(); i++) + for (auto const& i : rPrimitive2DSequence) { - drawinglayer::primitive2d::Primitive2DReference xPrimitive2DReference = rPrimitive2DSequence[i]; - const BasePrimitive2D* pBasePrimitive = dynamic_cast<const BasePrimitive2D* >(xPrimitive2DReference.get()); - if (!pBasePrimitive) - continue; + const BasePrimitive2D* pBasePrimitive = i.get(); sal_uInt32 nId = pBasePrimitive->getPrimitive2DID(); if (nId < maFilter.size() && maFilter[nId]) continue; + // handled by subclass + if (decomposeAndWrite(*pBasePrimitive, rWriter)) + continue; + OUString sCurrentElementTag = drawinglayer::primitive2d::idToString(nId); switch (nId) { + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: + { + const auto& rBitmapPrimitive2D + = static_cast<const BitmapPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("bitmap"); + writeMatrix(rWriter, rBitmapPrimitive2D.getTransform()); + + const Bitmap aBitmap(rBitmapPrimitive2D.getBitmap()); + const Size aSizePixel(aBitmap.GetSizePixel()); + + rWriter.attribute("height", aSizePixel.getHeight()); + rWriter.attribute("width", aSizePixel.getWidth()); + rWriter.attribute("checksum", OString(std::to_string(aBitmap.GetChecksum()))); + + for (tools::Long y = 0; y < aSizePixel.getHeight(); y++) + { + rWriter.startElement("data"); + OUString aBitmapData = u""_ustr; + for (tools::Long x = 0; x < aSizePixel.getWidth(); x++) + { + if (x != 0) + aBitmapData = aBitmapData + ","; + aBitmapData = aBitmapData + aBitmap.GetPixelColor(x, y).AsRGBHexString(); + } + rWriter.attribute("row", aBitmapData); + rWriter.endElement(); + } + rWriter.endElement(); + } + break; case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D: { - const HiddenGeometryPrimitive2D& rHiddenGeometryPrimitive2D = dynamic_cast<const HiddenGeometryPrimitive2D&>(*pBasePrimitive); + const auto& rHiddenGeometryPrimitive2D + = static_cast<const HiddenGeometryPrimitive2D&>(*pBasePrimitive); rWriter.startElement("hiddengeometry"); decomposeAndWrite(rHiddenGeometryPrimitive2D.getChildren(), rWriter); rWriter.endElement(); @@ -179,20 +741,10 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: { - const TransformPrimitive2D& rTransformPrimitive2D = dynamic_cast<const TransformPrimitive2D&>(*pBasePrimitive); + const auto& rTransformPrimitive2D + = static_cast<const TransformPrimitive2D&>(*pBasePrimitive); rWriter.startElement("transform"); - - basegfx::B2DHomMatrix const & rMatrix = rTransformPrimitive2D.getTransformation(); - rWriter.attributeDouble("xy11", rMatrix.get(0,0)); - rWriter.attributeDouble("xy12", rMatrix.get(0,1)); - rWriter.attributeDouble("xy13", rMatrix.get(0,2)); - rWriter.attributeDouble("xy21", rMatrix.get(1,0)); - rWriter.attributeDouble("xy22", rMatrix.get(1,1)); - rWriter.attributeDouble("xy23", rMatrix.get(1,2)); - rWriter.attributeDouble("xy31", rMatrix.get(2,0)); - rWriter.attributeDouble("xy32", rMatrix.get(2,1)); - rWriter.attributeDouble("xy33", rMatrix.get(2,2)); - + writeMatrix(rWriter, rTransformPrimitive2D.getTransformation()); decomposeAndWrite(rTransformPrimitive2D.getChildren(), rWriter); rWriter.endElement(); } @@ -200,33 +752,110 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: { - const PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D = dynamic_cast<const PolyPolygonColorPrimitive2D&>(*pBasePrimitive); + const auto& rPolyPolygonColorPrimitive2D + = static_cast<const PolyPolygonColorPrimitive2D&>(*pBasePrimitive); rWriter.startElement("polypolygoncolor"); - rWriter.attribute("color", convertColorToString(rPolyPolygonColorPrimitive2D.getBColor())); + rWriter.attribute("color", + convertColorToString(rPolyPolygonColorPrimitive2D.getBColor())); - const basegfx::B2DPolyPolygon& aB2DPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon()); + const basegfx::B2DPolyPolygon& aB2DPolyPolygon( + rPolyPolygonColorPrimitive2D.getB2DPolyPolygon()); writePolyPolygon(rWriter, aB2DPolyPolygon); rWriter.endElement(); } break; + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: + { + const auto& rPointArrayPrimitive2D + = static_cast<const PointArrayPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("pointarray"); - case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D: + rWriter.attribute("color", + convertColorToString(rPointArrayPrimitive2D.getRGBColor())); + + const std::vector<basegfx::B2DPoint> aPositions + = rPointArrayPrimitive2D.getPositions(); + for (std::vector<basegfx::B2DPoint>::const_iterator iter = aPositions.begin(); + iter != aPositions.end(); ++iter) + { + rWriter.startElement("point"); + rWriter.attribute("x", OUString::number(iter->getX())); + rWriter.attribute("y", OUString::number(iter->getY())); + rWriter.endElement(); + } + + rWriter.endElement(); + } + break; + + case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D: { - const PolyPolygonStrokePrimitive2D& rPolyPolygonStrokePrimitive2D = dynamic_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive); - rWriter.startElement("polypolygonstroke"); + const auto& rPolygonStrokeArrowPrimitive2D + = static_cast<const PolygonStrokeArrowPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polygonstrokearrow"); - rWriter.startElement("line"); - const drawinglayer::attribute::LineAttribute& aLineAttribute = rPolyPolygonStrokePrimitive2D.getLineAttribute(); - rWriter.attribute("color", convertColorToString(aLineAttribute.getColor())); - rWriter.attribute("width", aLineAttribute.getWidth()); - //rWriter.attribute("linejoin", aLineAttribute.getLineJoin()); - //rWriter.attribute("linecap", aLineAttribute.getLineCap()); + rWriter.startElement("polygon"); + rWriter.content(basegfx::utils::exportToSvgPoints( + rPolygonStrokeArrowPrimitive2D.getB2DPolygon())); rWriter.endElement(); - //getStrokeAttribute() + if (rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon().count()) + { + rWriter.startElement("linestartattribute"); + rWriter.attribute("width", + rPolygonStrokeArrowPrimitive2D.getStart().getWidth()); + rWriter.attribute("centered", + static_cast<sal_Int32>( + rPolygonStrokeArrowPrimitive2D.getStart().isCentered())); + writePolyPolygon(rWriter, + rPolygonStrokeArrowPrimitive2D.getStart().getB2DPolyPolygon()); + rWriter.endElement(); + } + + if (rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon().count()) + { + rWriter.startElement("lineendattribute"); + rWriter.attribute("width", rPolygonStrokeArrowPrimitive2D.getEnd().getWidth()); + rWriter.attribute("centered", + static_cast<sal_Int32>( + rPolygonStrokeArrowPrimitive2D.getEnd().isCentered())); + writePolyPolygon(rWriter, + rPolygonStrokeArrowPrimitive2D.getEnd().getB2DPolyPolygon()); + rWriter.endElement(); + } + + writeLineAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getLineAttribute()); + writeStrokeAttribute(rWriter, rPolygonStrokeArrowPrimitive2D.getStrokeAttribute()); + rWriter.endElement(); + } + break; + + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + const auto& rPolygonStrokePrimitive2D + = static_cast<const PolygonStrokePrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polygonstroke"); + + rWriter.startElement("polygon"); + rWriter.content( + basegfx::utils::exportToSvgPoints(rPolygonStrokePrimitive2D.getB2DPolygon())); + rWriter.endElement(); + + writeLineAttribute(rWriter, rPolygonStrokePrimitive2D.getLineAttribute()); + writeStrokeAttribute(rWriter, rPolygonStrokePrimitive2D.getStrokeAttribute()); + rWriter.endElement(); + } + break; + case PRIMITIVE2D_ID_POLYPOLYGONSTROKEPRIMITIVE2D: + { + const auto& rPolyPolygonStrokePrimitive2D + = static_cast<const PolyPolygonStrokePrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polypolygonstroke"); + writeLineAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getLineAttribute()); + writeStrokeAttribute(rWriter, rPolyPolygonStrokePrimitive2D.getStrokeAttribute()); writePolyPolygon(rWriter, rPolyPolygonStrokePrimitive2D.getB2DPolyPolygon()); rWriter.endElement(); @@ -235,46 +864,114 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: { - const PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D = dynamic_cast<const PolygonHairlinePrimitive2D&>(*pBasePrimitive); + const auto& rPolygonHairlinePrimitive2D + = static_cast<const PolygonHairlinePrimitive2D&>(*pBasePrimitive); rWriter.startElement("polygonhairline"); - rWriter.attribute("color", convertColorToString(rPolygonHairlinePrimitive2D.getBColor())); + rWriter.attribute("color", + convertColorToString(rPolygonHairlinePrimitive2D.getBColor())); rWriter.startElement("polygon"); - rWriter.content(basegfx::utils::exportToSvgPoints(rPolygonHairlinePrimitive2D.getB2DPolygon())); + rWriter.content( + basegfx::utils::exportToSvgPoints(rPolygonHairlinePrimitive2D.getB2DPolygon())); rWriter.endElement(); + rWriter.endElement(); + } + break; + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: + { + const auto& rTextDecoratedPortionPrimitive2D + = static_cast<const TextDecoratedPortionPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("textdecoratedportion"); + writeMatrix(rWriter, rTextDecoratedPortionPrimitive2D.getTextTransform()); + + rWriter.attribute("text", rTextDecoratedPortionPrimitive2D.getText()); + rWriter.attribute( + "fontcolor", + convertColorToString(rTextDecoratedPortionPrimitive2D.getFontColor())); + + const drawinglayer::attribute::FontAttribute& aFontAttribute + = rTextDecoratedPortionPrimitive2D.getFontAttribute(); + rWriter.attribute("familyname", aFontAttribute.getFamilyName()); + rWriter.endElement(); + } + break; + + case PRIMITIVE2D_ID_TEXTLINEPRIMITIVE2D: + { + const auto& rTextLinePrimitive2D + = static_cast<const TextLinePrimitive2D&>(*pBasePrimitive); + rWriter.startElement("textline"); + writeMatrix(rWriter, rTextLinePrimitive2D.getObjectTransformation()); + + rWriter.attribute("width", rTextLinePrimitive2D.getWidth()); + rWriter.attribute("offset", rTextLinePrimitive2D.getOffset()); + rWriter.attribute("height", rTextLinePrimitive2D.getHeight()); + rWriter.attribute("color", + convertColorToString(rTextLinePrimitive2D.getLineColor())); rWriter.endElement(); } break; case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: { - const TextSimplePortionPrimitive2D& rTextSimplePortionPrimitive2D = dynamic_cast<const TextSimplePortionPrimitive2D&>(*pBasePrimitive); + const auto& rTextSimplePortionPrimitive2D + = static_cast<const TextSimplePortionPrimitive2D&>(*pBasePrimitive); rWriter.startElement("textsimpleportion"); basegfx::B2DVector aScale, aTranslate; double fRotate, fShearX; - if(rTextSimplePortionPrimitive2D.getTextTransform().decompose(aScale, aTranslate, fRotate, fShearX)) + if (rTextSimplePortionPrimitive2D.getTextTransform().decompose(aScale, aTranslate, + fRotate, fShearX)) { rWriter.attribute("width", aScale.getX()); rWriter.attribute("height", aScale.getY()); } rWriter.attribute("x", aTranslate.getX()); rWriter.attribute("y", aTranslate.getY()); - rWriter.attribute("text", rTextSimplePortionPrimitive2D.getText()); - rWriter.attribute("fontcolor", convertColorToString(rTextSimplePortionPrimitive2D.getFontColor())); - - const drawinglayer::attribute::FontAttribute& aFontAttribute = rTextSimplePortionPrimitive2D.getFontAttribute(); + OUString aText = rTextSimplePortionPrimitive2D.getText(); + // TODO share code with sax_fastparser::FastSaxSerializer::write(). + rWriter.attribute("text", aText.replaceAll("", "	")); + rWriter.attribute("fontcolor", convertColorToString( + rTextSimplePortionPrimitive2D.getFontColor())); + + const drawinglayer::attribute::FontAttribute& aFontAttribute + = rTextSimplePortionPrimitive2D.getFontAttribute(); rWriter.attribute("familyname", aFontAttribute.getFamilyName()); + + if (aFontAttribute.getRTL()) + { + rWriter.attribute("rtl", std::u16string_view{ u"true" }); + } + + const std::vector<double> aDx = rTextSimplePortionPrimitive2D.getDXArray(); + if (aDx.size()) + { + for (size_t iDx = 0; iDx < aDx.size(); ++iDx) + { + OString sName = "dx" + OString::number(iDx); + rWriter.attribute(sName, OString::number(aDx[iDx])); + } + } + rWriter.endElement(); + } + break; + + case PRIMITIVE2D_ID_GROUPPRIMITIVE2D: + { + const auto& rGroupPrimitive2D + = static_cast<const GroupPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("group"); + decomposeAndWrite(rGroupPrimitive2D.getChildren(), rWriter); rWriter.endElement(); } break; case PRIMITIVE2D_ID_MASKPRIMITIVE2D: { - const MaskPrimitive2D& rMaskPrimitive2D = dynamic_cast<const MaskPrimitive2D&>(*pBasePrimitive); + const auto& rMaskPrimitive2D = static_cast<const MaskPrimitive2D&>(*pBasePrimitive); rWriter.startElement("mask"); writePolyPolygon(rWriter, rMaskPrimitive2D.getMask()); decomposeAndWrite(rMaskPrimitive2D.getChildren(), rWriter); @@ -284,18 +981,21 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: { - const UnifiedTransparencePrimitive2D& rUnifiedTransparencePrimitive2D = dynamic_cast<const UnifiedTransparencePrimitive2D&>(*pBasePrimitive); + const auto& rUnifiedTransparencePrimitive2D + = static_cast<const UnifiedTransparencePrimitive2D&>(*pBasePrimitive); rWriter.startElement("unifiedtransparence"); - rWriter.attribute("transparence", OString::number(rUnifiedTransparencePrimitive2D.getTransparence())); + rWriter.attribute( + "transparence", + std::lround(100 * rUnifiedTransparencePrimitive2D.getTransparence())); decomposeAndWrite(rUnifiedTransparencePrimitive2D.getChildren(), rWriter); - rWriter.endElement(); } break; case PRIMITIVE2D_ID_OBJECTINFOPRIMITIVE2D: { - const ObjectInfoPrimitive2D& rObjectInfoPrimitive2D = dynamic_cast<const ObjectInfoPrimitive2D&>(*pBasePrimitive); + const auto& rObjectInfoPrimitive2D + = static_cast<const ObjectInfoPrimitive2D&>(*pBasePrimitive); rWriter.startElement("objectinfo"); decomposeAndWrite(rObjectInfoPrimitive2D.getChildren(), rWriter); @@ -303,23 +1003,53 @@ void Primitive2dXmlDump::decomposeAndWrite( } break; + case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D: + { + const auto& rStructureTagPrimitive2D + = static_cast<const StructureTagPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("structuretag"); + rWriter.attribute("structureelement", + sal_Int32(rStructureTagPrimitive2D.getStructureElement())); + decomposeAndWrite(rStructureTagPrimitive2D.getChildren(), rWriter); + rWriter.endElement(); + } + break; + case PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D: { - const SvgRadialGradientPrimitive2D& rSvgRadialGradientPrimitive2D = dynamic_cast<const SvgRadialGradientPrimitive2D&>(*pBasePrimitive); + const auto& rSvgRadialGradientPrimitive2D + = static_cast<const SvgRadialGradientPrimitive2D&>(*pBasePrimitive); rWriter.startElement("svgradialgradient"); - basegfx::B2DPoint aFocusAttribute = rSvgRadialGradientPrimitive2D.getFocal(); + if (rSvgRadialGradientPrimitive2D.isFocalSet()) + { + basegfx::B2DPoint aFocalAttribute = rSvgRadialGradientPrimitive2D.getFocal(); + rWriter.attribute("focalx", aFocalAttribute.getX()); + rWriter.attribute("focaly", aFocalAttribute.getY()); + } - rWriter.attribute("radius", OString::number(rSvgRadialGradientPrimitive2D.getRadius())); - rWriter.attribute("focusx", aFocusAttribute.getX()); - rWriter.attribute("focusy", aFocusAttribute.getY()); + basegfx::B2DPoint aStartPoint = rSvgRadialGradientPrimitive2D.getStart(); + rWriter.attribute("startx", aStartPoint.getX()); + rWriter.attribute("starty", aStartPoint.getY()); + rWriter.attribute("radius", + OString::number(rSvgRadialGradientPrimitive2D.getRadius())); + writeSpreadMethod(rWriter, rSvgRadialGradientPrimitive2D.getSpreadMethod()); + rWriter.attributeDouble( + "opacity", + rSvgRadialGradientPrimitive2D.getGradientEntries().front().getOpacity()); + + rWriter.startElement("transform"); + writeMatrix(rWriter, rSvgRadialGradientPrimitive2D.getGradientTransform()); + rWriter.endElement(); + writePolyPolygon(rWriter, rSvgRadialGradientPrimitive2D.getPolyPolygon()); rWriter.endElement(); } break; case PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D: { - const SvgLinearGradientPrimitive2D& rSvgLinearGradientPrimitive2D = dynamic_cast<const SvgLinearGradientPrimitive2D&>(*pBasePrimitive); + const auto& rSvgLinearGradientPrimitive2D + = static_cast<const SvgLinearGradientPrimitive2D&>(*pBasePrimitive); rWriter.startElement("svglineargradient"); basegfx::B2DPoint aStartAttribute = rSvgLinearGradientPrimitive2D.getStart(); basegfx::B2DPoint aEndAttribute = rSvgLinearGradientPrimitive2D.getEnd(); @@ -328,20 +1058,13 @@ void Primitive2dXmlDump::decomposeAndWrite( rWriter.attribute("starty", aStartAttribute.getY()); rWriter.attribute("endx", aEndAttribute.getX()); rWriter.attribute("endy", aEndAttribute.getY()); - //rWriter.attribute("spreadmethod", (int)rSvgLinearGradientPrimitive2D.getSpreadMethod()); - rWriter.attributeDouble("opacity", rSvgLinearGradientPrimitive2D.getGradientEntries().front().getOpacity()); + writeSpreadMethod(rWriter, rSvgLinearGradientPrimitive2D.getSpreadMethod()); + rWriter.attributeDouble( + "opacity", + rSvgLinearGradientPrimitive2D.getGradientEntries().front().getOpacity()); rWriter.startElement("transform"); - basegfx::B2DHomMatrix const & rMatrix = rSvgLinearGradientPrimitive2D.getGradientTransform(); - rWriter.attributeDouble("xy11", rMatrix.get(0,0)); - rWriter.attributeDouble("xy12", rMatrix.get(0,1)); - rWriter.attributeDouble("xy13", rMatrix.get(0,2)); - rWriter.attributeDouble("xy21", rMatrix.get(1,0)); - rWriter.attributeDouble("xy22", rMatrix.get(1,1)); - rWriter.attributeDouble("xy23", rMatrix.get(1,2)); - rWriter.attributeDouble("xy31", rMatrix.get(2,0)); - rWriter.attributeDouble("xy32", rMatrix.get(2,1)); - rWriter.attributeDouble("xy33", rMatrix.get(2,2)); + writeMatrix(rWriter, rSvgLinearGradientPrimitive2D.getGradientTransform()); rWriter.endElement(); writePolyPolygon(rWriter, rSvgLinearGradientPrimitive2D.getPolyPolygon()); @@ -352,12 +1075,9 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: { - const MetafilePrimitive2D& rMetafilePrimitive2D = dynamic_cast<const MetafilePrimitive2D&>(*pBasePrimitive); rWriter.startElement("metafile"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; // since the graphic is not rendered in a document, we do not need a concrete view information - rMetafilePrimitive2D.get2DDecomposition(aPrimitiveContainer, drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer,rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); rWriter.endElement(); } @@ -367,10 +1087,7 @@ void Primitive2dXmlDump::decomposeAndWrite( { // SdrRectanglePrimitive2D is private to us. rWriter.startElement("sdrrectangle"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); rWriter.endElement(); break; } @@ -379,10 +1096,7 @@ void Primitive2dXmlDump::decomposeAndWrite( { // SdrBlockTextPrimitive2D is private to us. rWriter.startElement("sdrblocktext"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); rWriter.endElement(); break; } @@ -391,10 +1105,20 @@ void Primitive2dXmlDump::decomposeAndWrite( { // TextHierarchyBlockPrimitive2D. rWriter.startElement("texthierarchyblock"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); + rWriter.endElement(); + break; + } + + case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D: + { + rWriter.startElement("texthierarchyedit"); + runDecomposeAndRecurse(pBasePrimitive, rWriter); + const auto* pTextHierarchyEditPrimitive + = dynamic_cast<const drawinglayer::primitive2d::TextHierarchyEditPrimitive2D*>( + pBasePrimitive); + if (pTextHierarchyEditPrimitive) + decomposeAndWrite(pTextHierarchyEditPrimitive->getChildren(), rWriter); rWriter.endElement(); break; } @@ -403,10 +1127,7 @@ void Primitive2dXmlDump::decomposeAndWrite( { // TextHierarchyParagraphPrimitive2D. rWriter.startElement("texthierarchyparagraph"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); rWriter.endElement(); break; } @@ -415,10 +1136,7 @@ void Primitive2dXmlDump::decomposeAndWrite( { // TextHierarchyLinePrimitive2D. rWriter.startElement("texthierarchyline"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + runDecomposeAndRecurse(pBasePrimitive, rWriter); rWriter.endElement(); break; } @@ -426,11 +1144,107 @@ void Primitive2dXmlDump::decomposeAndWrite( case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D: { // ShadowPrimitive2D. + const auto& rShadowPrimitive2D + = static_cast<const ShadowPrimitive2D&>(*pBasePrimitive); rWriter.startElement("shadow"); - drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; - pBasePrimitive->get2DDecomposition(aPrimitiveContainer, - drawinglayer::geometry::ViewInformation2D()); - decomposeAndWrite(aPrimitiveContainer, rWriter); + rWriter.attribute("color", + convertColorToString(rShadowPrimitive2D.getShadowColor())); + rWriter.attributeDouble("blur", rShadowPrimitive2D.getShadowBlur()); + + rWriter.startElement("transform"); + writeMatrix(rWriter, rShadowPrimitive2D.getShadowTransform()); + rWriter.endElement(); + + rWriter.endElement(); + break; + } + + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: + { + // ModifiedColorPrimitive2D. + const auto& rModifiedColorPrimitive2D + = static_cast<const ModifiedColorPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("modifiedColor"); + const basegfx::BColorModifierSharedPtr& aColorModifier + = rModifiedColorPrimitive2D.getColorModifier(); + rWriter.attribute("modifier", aColorModifier->getModifierName()); + + decomposeAndWrite(rModifiedColorPrimitive2D.getChildren(), rWriter); + rWriter.endElement(); + break; + } + case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D: + { + // SoftEdgePrimitive2D. + const auto& rSoftEdgePrimitive2D + = static_cast<const SoftEdgePrimitive2D&>(*pBasePrimitive); + rWriter.startElement("softedge"); + rWriter.attribute("radius", OUString::number(rSoftEdgePrimitive2D.getRadius())); + + decomposeAndWrite(rSoftEdgePrimitive2D.getChildren(), rWriter); + rWriter.endElement(); + break; + } + + case PRIMITIVE2D_ID_SCENEPRIMITIVE2D: + { + const auto& rScenePrimitive2D + = static_cast<const drawinglayer::primitive2d::ScenePrimitive2D&>( + *pBasePrimitive); + rWriter.startElement("scene"); + + auto const& rSceneAttribute = rScenePrimitive2D.getSdrSceneAttribute(); + + rWriter.attribute("shadowSlant", rSceneAttribute.getShadowSlant()); + rWriter.attribute("isTwoSidedLighting", + sal_Int32(rSceneAttribute.getTwoSidedLighting())); + writeShadeMode(rWriter, rSceneAttribute.getShadeMode()); + writeProjectionMode(rWriter, rSceneAttribute.getProjectionMode()); + + auto const& rLightingAttribute = rScenePrimitive2D.getSdrLightingAttribute(); + rWriter.attribute("ambientLightColor", + convertColorToString(rLightingAttribute.getAmbientLightColor())); + rWriter.startElement("lights"); + for (auto const& rLight : rLightingAttribute.getLightVector()) + { + rWriter.startElement("light"); + rWriter.attribute("color", convertColorToString(rLight.getColor())); + rWriter.attribute("directionVectorX", rLight.getDirection().getX()); + rWriter.attribute("directionVectorY", rLight.getDirection().getY()); + rWriter.attribute("specular", sal_Int32(rLight.getSpecular())); + rWriter.endElement(); + } + rWriter.endElement(); + + Primitive3DXmlDump aPrimitive3DXmlDump; + aPrimitive3DXmlDump.decomposeAndWrite(rScenePrimitive2D.getChildren3D(), rWriter); + + rWriter.endElement(); + break; + } + + case PRIMITIVE2D_ID_POLYPOLYGONRGBAPRIMITIVE2D: + { + const PolyPolygonRGBAPrimitive2D& rPolyPolygonRGBAPrimitive2D + = dynamic_cast<const PolyPolygonRGBAPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polypolygonrgba"); + rWriter.attribute("color", + convertColorToString(rPolyPolygonRGBAPrimitive2D.getBColor())); + rWriter.attribute("transparence", + std::lround(100 * rPolyPolygonRGBAPrimitive2D.getTransparency())); + writePolyPolygon(rWriter, rPolyPolygonRGBAPrimitive2D.getB2DPolyPolygon()); + rWriter.endElement(); + break; + } + + case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D: + { + const PolyPolygonAlphaGradientPrimitive2D& rPolyPolygonAlphaGradientPrimitive2D + = dynamic_cast<const PolyPolygonAlphaGradientPrimitive2D&>(*pBasePrimitive); + rWriter.startElement("polypolygonalphagradient"); + rWriter.attribute("color", convertColorToString( + rPolyPolygonAlphaGradientPrimitive2D.getBColor())); + writePolyPolygon(rWriter, rPolyPolygonAlphaGradientPrimitive2D.getB2DPolyPolygon()); rWriter.endElement(); break; } @@ -438,7 +1252,9 @@ void Primitive2dXmlDump::decomposeAndWrite( default: { rWriter.startElement("unhandled"); - rWriter.attribute("id", OUStringToOString(sCurrentElementTag, RTL_TEXTENCODING_UTF8)); + rWriter.attribute("id", sCurrentElementTag); + rWriter.attribute("idNumber", nId); + drawinglayer::primitive2d::Primitive2DContainer aPrimitiveContainer; pBasePrimitive->get2DDecomposition(aPrimitiveContainer, drawinglayer::geometry::ViewInformation2D()); @@ -447,7 +1263,6 @@ void Primitive2dXmlDump::decomposeAndWrite( } break; } - } } diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx b/drawinglayer/source/tools/wmfemfhelper.cxx index cfab0426ef74..5a983e57afdd 100644 --- a/drawinglayer/source/tools/wmfemfhelper.cxx +++ b/drawinglayer/source/tools/wmfemfhelper.cxx @@ -19,19 +19,17 @@ #include <wmfemfhelper.hxx> #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> +#include <vcl/alpha.hxx> #include <vcl/lineinfo.hxx> #include <vcl/metaact.hxx> -#include <drawinglayer/primitive2d/polygonprimitive2d.hxx> #include <drawinglayer/primitive2d/transformprimitive2d.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> -#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx> #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> @@ -52,6 +50,7 @@ #include <sal/log.hxx> #include <tools/fract.hxx> #include <tools/stream.hxx> +#include <tools/UnitConversion.hxx> #include <vcl/canvastools.hxx> #include <vcl/gradient.hxx> #include <vcl/hatch.hxx> @@ -79,7 +78,7 @@ namespace drawinglayer::primitive2d { protected: /// local decomposition. - virtual void create2DDecomposition(Primitive2DContainer& rContainer, + virtual Primitive2DReference create2DDecomposition( const geometry::ViewInformation2D& rViewInformation) const override; public: @@ -94,14 +93,14 @@ namespace drawinglayer::primitive2d } - void NonOverlappingFillGradientPrimitive2D::create2DDecomposition( - Primitive2DContainer& rContainer, + Primitive2DReference NonOverlappingFillGradientPrimitive2D::create2DDecomposition( const geometry::ViewInformation2D& /*rViewInformation*/) const { if (!getFillGradient().isDefault()) { - createFill(rContainer, false); + return createFill(false); } + return nullptr; } } // end of namespace @@ -115,20 +114,12 @@ namespace wmfemfhelper interpretation of the MetaFile action flow. */ PropertyHolder::PropertyHolder() - : maTransformation(), - maMapUnit(MapUnit::Map100thMM), - maLineColor(), - maFillColor(), + : maMapUnit(MapUnit::Map100thMM), maTextColor(sal_uInt32(COL_BLACK)), - maTextFillColor(), - maTextLineColor(), - maOverlineColor(), - maClipPolyPoygon(), - maFont(), maRasterOp(RasterOp::OverPaint), - mnLayoutMode(ComplexTextLayoutFlags::Default), + mnLayoutMode(vcl::text::ComplexTextLayoutFlags::Default), maLanguageType(0), - mnPushFlags(PushFlags::NONE), + mnPushFlags(vcl::PushFlags::NONE), mbLineColor(false), mbFillColor(false), mbTextColor(true), @@ -162,7 +153,7 @@ namespace wmfemfhelper maPropertyHolders.push_back(pNew); } - void PropertyHolders::Push(PushFlags nPushFlags) + void PropertyHolders::Push(vcl::PushFlags nPushFlags) { if (bool(nPushFlags)) { @@ -185,56 +176,56 @@ namespace wmfemfhelper return; const PropertyHolder* pTip = maPropertyHolders.back(); - const PushFlags nPushFlags(pTip->getPushFlags()); + const vcl::PushFlags nPushFlags(pTip->getPushFlags()); - if (nPushFlags != PushFlags::NONE) + if (nPushFlags != vcl::PushFlags::NONE) { if (nSize > 1) { // copy back content for all non-set flags PropertyHolder* pLast = maPropertyHolders[nSize - 2]; - if (PushFlags::ALL != nPushFlags) + if (vcl::PushFlags::ALL != nPushFlags) { - if (!(nPushFlags & PushFlags::LINECOLOR)) + if (!(nPushFlags & vcl::PushFlags::LINECOLOR)) { pLast->setLineColor(pTip->getLineColor()); pLast->setLineColorActive(pTip->getLineColorActive()); } - if (!(nPushFlags & PushFlags::FILLCOLOR)) + if (!(nPushFlags & vcl::PushFlags::FILLCOLOR)) { pLast->setFillColor(pTip->getFillColor()); pLast->setFillColorActive(pTip->getFillColorActive()); } - if (!(nPushFlags & PushFlags::FONT)) + if (!(nPushFlags & vcl::PushFlags::FONT)) { pLast->setFont(pTip->getFont()); } - if (!(nPushFlags & PushFlags::TEXTCOLOR)) + if (!(nPushFlags & vcl::PushFlags::TEXTCOLOR)) { pLast->setTextColor(pTip->getTextColor()); pLast->setTextColorActive(pTip->getTextColorActive()); } - if (!(nPushFlags & PushFlags::MAPMODE)) + if (!(nPushFlags & vcl::PushFlags::MAPMODE)) { pLast->setTransformation(pTip->getTransformation()); pLast->setMapUnit(pTip->getMapUnit()); } - if (!(nPushFlags & PushFlags::CLIPREGION)) + if (!(nPushFlags & vcl::PushFlags::CLIPREGION)) { pLast->setClipPolyPolygon(pTip->getClipPolyPolygon()); pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive()); } - if (!(nPushFlags & PushFlags::RASTEROP)) + if (!(nPushFlags & vcl::PushFlags::RASTEROP)) { pLast->setRasterOp(pTip->getRasterOp()); } - if (!(nPushFlags & PushFlags::TEXTFILLCOLOR)) + if (!(nPushFlags & vcl::PushFlags::TEXTFILLCOLOR)) { pLast->setTextFillColor(pTip->getTextFillColor()); pLast->setTextFillColorActive(pTip->getTextFillColorActive()); } - if (!(nPushFlags & PushFlags::TEXTALIGN)) + if (!(nPushFlags & vcl::PushFlags::TEXTALIGN)) { if (pLast->getFont().GetAlignment() != pTip->getFont().GetAlignment()) { @@ -243,24 +234,24 @@ namespace wmfemfhelper pLast->setFont(aFont); } } - if (!(nPushFlags & PushFlags::REFPOINT)) + if (!(nPushFlags & vcl::PushFlags::REFPOINT)) { // not supported } - if (!(nPushFlags & PushFlags::TEXTLINECOLOR)) + if (!(nPushFlags & vcl::PushFlags::TEXTLINECOLOR)) { pLast->setTextLineColor(pTip->getTextLineColor()); pLast->setTextLineColorActive(pTip->getTextLineColorActive()); } - if (!(nPushFlags & PushFlags::TEXTLAYOUTMODE)) + if (!(nPushFlags & vcl::PushFlags::TEXTLAYOUTMODE)) { pLast->setLayoutMode(pTip->getLayoutMode()); } - if (!(nPushFlags & PushFlags::TEXTLANGUAGE)) + if (!(nPushFlags & vcl::PushFlags::TEXTLANGUAGE)) { pLast->setLanguageType(pTip->getLanguageType()); } - if (!(nPushFlags & PushFlags::OVERLINECOLOR)) + if (!(nPushFlags & vcl::PushFlags::OVERLINECOLOR)) { pLast->setOverlineColor(pTip->getOverlineColor()); pLast->setOverlineColorActive(pTip->getOverlineColorActive()); @@ -322,7 +313,6 @@ namespace wmfemfhelper data. */ TargetHolder::TargetHolder() - : aTargets() { } @@ -335,25 +325,17 @@ namespace wmfemfhelper return aTargets.size(); } - void TargetHolder::append(std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> pCandidate) + void TargetHolder::append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate) { if (pCandidate) { - aTargets.push_back(std::move(pCandidate)); + aTargets.push_back(pCandidate); } } drawinglayer::primitive2d::Primitive2DContainer TargetHolder::getPrimitive2DSequence(const PropertyHolder& rPropertyHolder) { - const sal_uInt32 nCount(aTargets.size()); - drawinglayer::primitive2d::Primitive2DContainer xRetval(nCount); - - for (sal_uInt32 a(0); a < nCount; a++) - { - xRetval[a] = aTargets[a].release(); - } - // Since we have released them from the list - aTargets.clear(); + drawinglayer::primitive2d::Primitive2DContainer xRetval = std::move(aTargets); if (!xRetval.empty() && rPropertyHolder.getClipPolyPolygonActive()) { @@ -361,12 +343,11 @@ namespace wmfemfhelper if (rClipPolyPolygon.count()) { - const drawinglayer::primitive2d::Primitive2DReference xMask( - new drawinglayer::primitive2d::MaskPrimitive2D( - rClipPolyPolygon, - xRetval)); - - xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xMask }; + xRetval = drawinglayer::primitive2d::Primitive2DContainer{ + new drawinglayer::primitive2d::MaskPrimitive2D( + rClipPolyPolygon, + std::move(xRetval)) + }; } } @@ -448,7 +429,7 @@ namespace wmfemfhelper { /** helper to create a PointArrayPrimitive2D based on current context */ static void createPointArrayPrimitive( - const std::vector< basegfx::B2DPoint >& rPositions, + std::vector< basegfx::B2DPoint >&& rPositions, TargetHolder& rTarget, PropertyHolder const & rProperties, const basegfx::BColor& rBColor) @@ -459,22 +440,20 @@ namespace wmfemfhelper if(rProperties.getTransformation().isIdentity()) { rTarget.append( - std::make_unique<drawinglayer::primitive2d::PointArrayPrimitive2D>( - rPositions, + new drawinglayer::primitive2d::PointArrayPrimitive2D( + std::move(rPositions), rBColor)); } else { - std::vector< basegfx::B2DPoint > aPositions(rPositions); - - for(basegfx::B2DPoint & aPosition : aPositions) + for(basegfx::B2DPoint & aPosition : rPositions) { aPosition = rProperties.getTransformation() * aPosition; } rTarget.append( - std::make_unique<drawinglayer::primitive2d::PointArrayPrimitive2D>( - aPositions, + new drawinglayer::primitive2d::PointArrayPrimitive2D( + std::move(rPositions), rBColor)); } } @@ -490,8 +469,8 @@ namespace wmfemfhelper basegfx::B2DPolygon aLinePolygon(rLinePolygon); aLinePolygon.transform(rProperties.getTransformation()); rTarget.append( - std::make_unique<drawinglayer::primitive2d::PolygonHairlinePrimitive2D>( - aLinePolygon, + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + std::move(aLinePolygon), rProperties.getLineColor())); } } @@ -507,8 +486,8 @@ namespace wmfemfhelper basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon); aFillPolyPolygon.transform(rProperties.getTransformation()); rTarget.append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - aFillPolyPolygon, + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + std::move(aFillPolyPolygon), rProperties.getFillColor())); } } @@ -530,7 +509,7 @@ namespace wmfemfhelper { basegfx::B2DPolygon aLinePolygon(rLinePolygon); aLinePolygon.transform(rProperties.getTransformation()); - const drawinglayer::attribute::LineAttribute aLineAttribute( + drawinglayer::attribute::LineAttribute aLineAttribute( rProperties.getLineColor(), bWidthUsed ? rLineInfo.GetWidth() : 0.0, rLineInfo.GetLineJoin(), @@ -538,39 +517,23 @@ namespace wmfemfhelper if(bDashDotUsed) { - std::vector< double > fDotDashArray; - const double fDashLen(rLineInfo.GetDashLen()); - const double fDotLen(rLineInfo.GetDotLen()); - const double fDistance(rLineInfo.GetDistance()); - - for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++) - { - fDotDashArray.push_back(fDashLen); - fDotDashArray.push_back(fDistance); - } - - for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++) - { - fDotDashArray.push_back(fDotLen); - fDotDashArray.push_back(fDistance); - } - + std::vector< double > fDotDashArray = rLineInfo.GetDotDashArray(); const double fAccumulated(std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0)); - const drawinglayer::attribute::StrokeAttribute aStrokeAttribute( - fDotDashArray, + drawinglayer::attribute::StrokeAttribute aStrokeAttribute( + std::move(fDotDashArray), fAccumulated); rTarget.append( - std::make_unique<drawinglayer::primitive2d::PolygonStrokePrimitive2D>( - aLinePolygon, - aLineAttribute, - aStrokeAttribute)); + new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + std::move(aLinePolygon), + std::move(aLineAttribute), + std::move(aStrokeAttribute))); } else { rTarget.append( - std::make_unique<drawinglayer::primitive2d::PolygonStrokePrimitive2D>( - aLinePolygon, + new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + std::move(aLinePolygon), aLineAttribute)); } } @@ -623,33 +586,33 @@ namespace wmfemfhelper position and size in pixels. At the end it will create a view-dependent transformed embedding of a BitmapPrimitive2D. */ - static void createBitmapExPrimitive( - const BitmapEx& rBitmapEx, + static void createBitmapPrimitive( + const Bitmap& rBitmap, const Point& rPoint, TargetHolder& rTarget, PropertyHolder const & rProperties) { - if(!rBitmapEx.IsEmpty()) + if(!rBitmap.IsEmpty()) { basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y()); aPoint = rProperties.getTransformation() * aPoint; rTarget.append( - std::make_unique<drawinglayer::primitive2d::DiscreteBitmapPrimitive2D>( - rBitmapEx, + new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D( + rBitmap, aPoint)); } } /** helper to create BitmapPrimitive2D based on current context */ - static void createBitmapExPrimitive( - const BitmapEx& rBitmapEx, + static void createBitmapPrimitive( + const Bitmap& rBitmap, const Point& rPoint, const Size& rSize, TargetHolder& rTarget, PropertyHolder const & rProperties) { - if(rBitmapEx.IsEmpty()) + if(rBitmap.IsEmpty()) return; basegfx::B2DHomMatrix aObjectTransform; @@ -662,8 +625,8 @@ namespace wmfemfhelper aObjectTransform = rProperties.getTransformation() * aObjectTransform; rTarget.append( - std::make_unique<drawinglayer::primitive2d::BitmapPrimitive2D>( - VCLUnoHelper::CreateVCLXBitmap(rBitmapEx), + new drawinglayer::primitive2d::BitmapPrimitive2D( + rBitmap, aObjectTransform)); } @@ -671,20 +634,19 @@ namespace wmfemfhelper which use a bitmap without transparence but define one of the colors as transparent) */ - static BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor) + static Bitmap createMaskBmp(const Bitmap& rBitmap, const Color& rMaskColor) { const Color aWhite(COL_WHITE); - BitmapPalette aBiLevelPalette(2); - - aBiLevelPalette[0] = aWhite; - aBiLevelPalette[1] = rMaskColor; + BitmapPalette aBiLevelPalette { + aWhite, rMaskColor + }; - Bitmap aMask(rBitmap.CreateMask(aWhite)); - Bitmap aSolid(rBitmap.GetSizePixel(), 1, &aBiLevelPalette); + AlphaMask aMask(rBitmap.CreateAlphaMask(aWhite)); + Bitmap aSolid(rBitmap.GetSizePixel(), vcl::PixelFormat::N8_BPP, &aBiLevelPalette); aSolid.Erase(rMaskColor); - return BitmapEx(aSolid, aMask); + return Bitmap(aSolid, aMask); } /** helper to convert from a VCL Gradient definition to the corresponding @@ -712,50 +674,13 @@ namespace wmfemfhelper aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01); } - drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GradientStyle::Rect); - - switch(rGradient.GetStyle()) - { - case GradientStyle::Linear : - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Linear; - break; - } - case GradientStyle::Axial : - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Axial; - break; - } - case GradientStyle::Radial : - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Radial; - break; - } - case GradientStyle::Elliptical : - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical; - break; - } - case GradientStyle::Square : - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Square; - break; - } - default : // GradientStyle::Rect - { - aGradientStyle = drawinglayer::attribute::GradientStyle::Rect; - break; - } - } - return drawinglayer::attribute::FillGradientAttribute( - aGradientStyle, + rGradient.GetStyle(), static_cast<double>(rGradient.GetBorder()) * 0.01, static_cast<double>(rGradient.GetOfsX()) * 0.01, static_cast<double>(rGradient.GetOfsY()) * 0.01, - static_cast<double>(rGradient.GetAngle()) * F_PI1800, - aStart, - aEnd, + toRadians(rGradient.GetAngle()), + basegfx::BColorStops(aStart, aEnd), rGradient.GetSteps()); } @@ -788,7 +713,7 @@ namespace wmfemfhelper return drawinglayer::attribute::FillHatchAttribute( aHatchStyle, static_cast<double>(rHatch.GetDistance()), - static_cast<double>(rHatch.GetAngle()) * F_PI1800, + toRadians(rHatch.GetAngle()), rHatch.GetColor().getBColor(), 3, // same default as VCL, a minimum of three discrete units (pixels) offset false); @@ -858,8 +783,8 @@ namespace wmfemfhelper if(!aSubContent.empty()) { rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::GroupPrimitive2D>( - aSubContent)); + new drawinglayer::primitive2d::GroupPrimitive2D( + std::move(aSubContent))); } } @@ -905,8 +830,8 @@ namespace wmfemfhelper { // force content to black rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::ModifiedColorPrimitive2D>( - aSubContent, + new drawinglayer::primitive2d::ModifiedColorPrimitive2D( + std::move(aSubContent), std::make_shared<basegfx::BColorModifier_replace>( basegfx::BColor(0.0, 0.0, 0.0)))); } @@ -914,8 +839,8 @@ namespace wmfemfhelper { // invert content rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::InvertPrimitive2D>( - aSubContent)); + new drawinglayer::primitive2d::InvertPrimitive2D( + std::move(aSubContent))); } } } @@ -934,7 +859,7 @@ namespace wmfemfhelper /** helper to create needed data to emulate the VCL Wallpaper Metafile action. It is a quite mighty action. This helper is for simple color filled background. */ - static std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> CreateColorWallpaper( + static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateColorWallpaper( const basegfx::B2DRange& rRange, const basegfx::BColor& rColor, PropertyHolder const & rPropertyHolder) @@ -942,7 +867,7 @@ namespace wmfemfhelper basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(rRange)); aOutline.transform(rPropertyHolder.getTransformation()); - return std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( + return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( basegfx::B2DPolyPolygon(aOutline), rColor); } @@ -950,34 +875,35 @@ namespace wmfemfhelper /** helper to create needed data to emulate the VCL Wallpaper Metafile action. It is a quite mighty action. This helper is for gradient filled background. */ - static std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> CreateGradientWallpaper( + static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CreateGradientWallpaper( const basegfx::B2DRange& rRange, const Gradient& rGradient, PropertyHolder const & rPropertyHolder) { - const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + basegfx::BColor aSingleColor; - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if (aAttribute.getColorStops().isSingleColor(aSingleColor)) { // not really a gradient. Create filled rectangle - return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder); + return CreateColorWallpaper(rRange, aSingleColor, rPropertyHolder); } else { // really a gradient - std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D> pRetval( + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pRetval( new drawinglayer::primitive2d::FillGradientPrimitive2D( rRange, - aAttribute)); + std::move(aAttribute))); if(!rPropertyHolder.getTransformation().isIdentity()) { - const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval.release()); - const drawinglayer::primitive2d::Primitive2DContainer xSeq { xPrim }; + const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval); + drawinglayer::primitive2d::Primitive2DContainer xSeq { xPrim }; - pRetval.reset(new drawinglayer::primitive2d::TransformPrimitive2D( + pRetval = new drawinglayer::primitive2d::TransformPrimitive2D( rPropertyHolder.getTransformation(), - xSeq)); + std::move(xSeq)); } return pRetval; @@ -996,12 +922,12 @@ namespace wmfemfhelper TargetHolder& rTarget, PropertyHolder const & rProperty) { - const BitmapEx aBitmapEx(rWallpaper.GetBitmap()); + const Bitmap& aBitmap(rWallpaper.GetBitmap()); const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle()); // if bitmap visualisation is transparent, maybe background // needs to be filled. Create background - if(aBitmapEx.IsTransparent() + if(aBitmap.HasAlpha() || (WallpaperStyle::Tile != eWallpaperStyle && WallpaperStyle::Scale != eWallpaperStyle)) { if(rWallpaper.IsGradient()) @@ -1012,7 +938,7 @@ namespace wmfemfhelper rWallpaper.GetGradient(), rProperty)); } - else if(!rWallpaper.GetColor().GetTransparency()) + else if(!rWallpaper.GetColor().IsTransparent()) { rTarget.append( CreateColorWallpaper( @@ -1028,26 +954,24 @@ namespace wmfemfhelper aWallpaperRange = vcl::unotools::b2DRectangleFromRectangle(rWallpaper.GetRect()); } - drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill = + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBitmapWallpaperFill = new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D( aWallpaperRange, - aBitmapEx, + aBitmap, eWallpaperStyle); if(rProperty.getTransformation().isIdentity()) { // add directly - rTarget.append(std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D>(pBitmapWallpaperFill)); + rTarget.append(pBitmapWallpaperFill); } else { // when a transformation is set, embed to it - const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill); - rTarget.append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( rProperty.getTransformation(), - drawinglayer::primitive2d::Primitive2DContainer { xPrim })); + drawinglayer::primitive2d::Primitive2DContainer { pBitmapWallpaperFill })); } } @@ -1075,8 +999,8 @@ namespace wmfemfhelper rFontAttribute = drawinglayer::primitive2d::getFontAttributeFromVclFont( aFontScaling, rFont, - bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiRtl), - bool(rProperty.getLayoutMode() & ComplexTextLayoutFlags::BiDiStrong)); + bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiRtl), + bool(rProperty.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiStrong)); // add FontScaling rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY()); @@ -1102,7 +1026,7 @@ namespace wmfemfhelper // add FontRotation (if used) if(rFont.GetOrientation()) { - rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800); + rTextTransform.rotate(-toRadians(rFont.GetOrientation())); } } @@ -1114,11 +1038,12 @@ namespace wmfemfhelper const OUString& rText, sal_uInt16 nTextStart, sal_uInt16 nTextLength, - const std::vector< double >& rDXArray, + std::vector< double >&& rDXArray, + std::vector< sal_Bool >&& rKashidaArray, TargetHolder& rTarget, PropertyHolder const & rProperty) { - drawinglayer::primitive2d::BasePrimitive2D* pResult = nullptr; + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pResult; const vcl::Font& rFont = rProperty.getFont(); basegfx::B2DVector aAlignmentOffset(0.0, 0.0); @@ -1140,7 +1065,7 @@ namespace wmfemfhelper // prepare FontColor and Locale const basegfx::BColor aFontColor(rProperty.getTextColor()); const Color aFillColor(rFont.GetFillColor()); - const css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale()); + css::lang::Locale aLocale(LanguageTag(rProperty.getLanguageType()).getLocale()); const bool bWordLineMode(rFont.IsWordLineMode()); const bool bDecoratedIsNeeded( @@ -1198,7 +1123,8 @@ namespace wmfemfhelper rText, nTextStart, nTextLength, - rDXArray, + std::vector(rDXArray), + std::move(rKashidaArray), aFontAttribute, aLocale, aFontColor, @@ -1226,9 +1152,10 @@ namespace wmfemfhelper rText, nTextStart, nTextLength, - rDXArray, - aFontAttribute, - aLocale, + std::vector(rDXArray), + std::move(rKashidaArray), + std::move(aFontAttribute), + std::move(aLocale), aFontColor); } } @@ -1242,7 +1169,7 @@ namespace wmfemfhelper // get text width double fTextWidth(0.0); - if(rDXArray.empty()) + if (rDXArray.empty()) { fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength); } @@ -1251,7 +1178,7 @@ namespace wmfemfhelper fTextWidth = rDXArray.back(); } - if(basegfx::fTools::more(fTextWidth, 0.0)) + if (fTextWidth > 0.0 && !basegfx::fTools::equalZero(fTextWidth)) { // build text range const basegfx::B2DRange aTextRange( @@ -1265,14 +1192,14 @@ namespace wmfemfhelper if(rFont.GetOrientation()) { - aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800); + aTextTransform.rotate(-toRadians(rFont.GetOrientation())); } aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y()); // prepare Primitive2DSequence, put text in foreground drawinglayer::primitive2d::Primitive2DContainer aSequence(2); - aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult); + aSequence[1] = pResult; // prepare filled polygon basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aTextRange)); @@ -1284,7 +1211,7 @@ namespace wmfemfhelper rProperty.getTextFillColor())); // set as group at pResult - pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence); + pResult = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aSequence)); } } @@ -1294,22 +1221,20 @@ namespace wmfemfhelper // add created text primitive to target if(rProperty.getTransformation().isIdentity()) { - rTarget.append(std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D>(pResult)); + rTarget.append(pResult); } else { // when a transformation is set, embed to it - const drawinglayer::primitive2d::Primitive2DReference aReference(pResult); - rTarget.append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( rProperty.getTransformation(), - drawinglayer::primitive2d::Primitive2DContainer { aReference })); + drawinglayer::primitive2d::Primitive2DContainer { pResult })); } } /** helper which takes complete care for creating the needed textLine primitives */ - static void proccessMetaTextLineAction( + static void processMetaTextLineAction( const MetaTextLineAction& rAction, TargetHolder& rTarget, PropertyHolder const & rProperty) @@ -1330,7 +1255,7 @@ namespace wmfemfhelper if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)) return; - std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector; + drawinglayer::primitive2d::Primitive2DContainer aTargets; basegfx::B2DVector aAlignmentOffset(0.0, 0.0); drawinglayer::attribute::FontAttribute aFontAttribute; basegfx::B2DHomMatrix aTextTransform; @@ -1352,7 +1277,7 @@ namespace wmfemfhelper if(bOverlineUsed) { // create primitive geometry for overline - aTargetVector.push_back( + aTargets.push_back( new drawinglayer::primitive2d::TextLinePrimitive2D( aTextTransform, fLineWidth, @@ -1365,7 +1290,7 @@ namespace wmfemfhelper if(bUnderlineUsed) { // create primitive geometry for underline - aTargetVector.push_back( + aTargets.push_back( new drawinglayer::primitive2d::TextLinePrimitive2D( aTextTransform, fLineWidth, @@ -1384,22 +1309,22 @@ namespace wmfemfhelper // strikeout with character const sal_Unicode aStrikeoutChar( drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X'); - const css::lang::Locale aLocale(LanguageTag( + css::lang::Locale aLocale(LanguageTag( rProperty.getLanguageType()).getLocale()); - aTargetVector.push_back( + aTargets.push_back( new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D( aTextTransform, fLineWidth, rProperty.getTextColor(), aStrikeoutChar, - aFontAttribute, - aLocale)); + std::move(aFontAttribute), + std::move(aLocale))); } else { // strikeout with geometry - aTargetVector.push_back( + aTargets.push_back( new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D( aTextTransform, fLineWidth, @@ -1410,31 +1335,21 @@ namespace wmfemfhelper } } - if(aTargetVector.empty()) + if(aTargets.empty()) return; // add created text primitive to target if(rProperty.getTransformation().isIdentity()) { - for(drawinglayer::primitive2d::BasePrimitive2D* a : aTargetVector) - { - rTarget.append(std::unique_ptr<drawinglayer::primitive2d::BasePrimitive2D>(a)); - } + rTarget.append(std::move(aTargets)); } else { // when a transformation is set, embed to it - drawinglayer::primitive2d::Primitive2DContainer xTargets(aTargetVector.size()); - - for(size_t a(0); a < aTargetVector.size(); a++) - { - xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]); - } - rTarget.append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( rProperty.getTransformation(), - xTargets)); + std::move(aTargets))); } } @@ -1508,7 +1423,7 @@ namespace wmfemfhelper { if(!aPositions.empty()) { - createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); + createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); aPositions.clear(); } @@ -1524,7 +1439,7 @@ namespace wmfemfhelper if(!aPositions.empty()) { - createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); + createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor()); } break; @@ -1548,7 +1463,7 @@ namespace wmfemfhelper if(!aPositions.empty()) { - createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor()); + createPointArrayPrimitive(std::move(aPositions), rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor()); } } @@ -1579,7 +1494,6 @@ namespace wmfemfhelper } else { - aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current()); aLinePolygon.clear(); aLineInfo = pA->GetLineInfo(); @@ -1594,16 +1508,14 @@ namespace wmfemfhelper aLinePolygon.append(aEnd); } - nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction); + nAction++; + if (nAction < nCount) + pAction = rMetaFile.GetAction(nAction); } nAction--; - - if(aLinePolygon.count()) - { - aLineInfo.SetLineJoin(basegfx::B2DLineJoin::NONE); // It were lines; force to NONE + if (aLinePolygon.count()) createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current()); - } } break; @@ -1657,8 +1569,8 @@ namespace wmfemfhelper { double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); - fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); - fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); + fRadiusX = std::clamp(fRadiusX, 0.0, 1.0); + fRadiusY = std::clamp(fRadiusY, 0.0, 1.0); aOutline = basegfx::utils::createPolygonFromRect(aRange, fRadiusX, fRadiusY); } @@ -1814,13 +1726,14 @@ namespace wmfemfhelper if(nTextLength && rPropertyHolders.Current().getTextColorActive()) { - const std::vector< double > aDXArray{}; + std::vector< double > aDXArray{}; processMetaTextAction( pA->GetPoint(), pA->GetText(), nTextIndex, nTextLength, - aDXArray, + std::move(aDXArray), + {}, rTargetHolders.Current(), rPropertyHolders.Current()); } @@ -1844,15 +1757,16 @@ namespace wmfemfhelper { // prepare DXArray (if used) std::vector< double > aDXArray; - long* pDXArray = pA->GetDXArray(); + const KernArray& rDXArray = pA->GetDXArray(); + std::vector< sal_Bool > aKashidaArray = pA->GetKashidaArray(); - if(pDXArray) + if(!rDXArray.empty()) { aDXArray.reserve(nTextLength); for(sal_uInt32 a(0); a < nTextLength; a++) { - aDXArray.push_back(static_cast<double>(*(pDXArray + a))); + aDXArray.push_back(static_cast<double>(rDXArray[a])); } } @@ -1861,7 +1775,8 @@ namespace wmfemfhelper pA->GetText(), nTextIndex, nTextLength, - aDXArray, + std::move(aDXArray), + std::move(aKashidaArray), rTargetHolders.Current(), rPropertyHolders.Current()); } @@ -1924,7 +1839,8 @@ namespace wmfemfhelper pA->GetText(), nTextIndex, nTextLength, - aTextArray, + std::move(aTextArray), + {}, rTargetHolders.Current(), rPropertyHolders.Current()); } @@ -1982,9 +1898,9 @@ namespace wmfemfhelper { // add with transformation rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( rPropertyHolders.Current().getTransformation(), - xSubContent)); + std::move(xSubContent))); } } } @@ -1995,9 +1911,9 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL */ const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction); - const BitmapEx aBitmapEx(pA->GetBitmap()); + const Bitmap aBitmap(pA->GetBitmap()); - createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aBitmap, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2005,9 +1921,9 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL */ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction); - const BitmapEx aBitmapEx(pA->GetBitmap()); + const Bitmap aBitmap(pA->GetBitmap()); - createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aBitmap, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2027,8 +1943,7 @@ namespace wmfemfhelper aCroppedBitmap.Crop(aCropRectangle); } - const BitmapEx aCroppedBitmapEx(aCroppedBitmap); - createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aCroppedBitmap, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); } break; @@ -2037,9 +1952,9 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction); - const BitmapEx& rBitmapEx = pA->GetBitmapEx(); + const Bitmap& rBitmap = pA->GetBitmap(); - createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(rBitmap, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2047,9 +1962,9 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction); - const BitmapEx& rBitmapEx = pA->GetBitmapEx(); + const Bitmap& rBitmap = pA->GetBitmap(); - createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(rBitmap, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2057,19 +1972,19 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */ const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction); - const BitmapEx& rBitmapEx = pA->GetBitmapEx(); + const Bitmap& rBitmap = pA->GetBitmap(); - if(!rBitmapEx.IsEmpty()) + if(!rBitmap.IsEmpty()) { - BitmapEx aCroppedBitmapEx(rBitmapEx); + Bitmap aCroppedBitmap(rBitmap); const tools::Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); if(!aCropRectangle.IsEmpty()) { - aCroppedBitmapEx.Crop(aCropRectangle); + aCroppedBitmap.Crop(aCropRectangle); } - createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aCroppedBitmap, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); } break; @@ -2079,9 +1994,9 @@ namespace wmfemfhelper /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */ /** Huh, no it isn't!? */ const MetaMaskAction* pA = static_cast<const MetaMaskAction*>(pAction); - const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); + const Bitmap aBitmap(createMaskBmp(pA->GetBitmap(), pA->GetColor())); - createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aBitmap, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2089,9 +2004,9 @@ namespace wmfemfhelper { /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */ const MetaMaskScaleAction* pA = static_cast<const MetaMaskScaleAction*>(pAction); - const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor())); + const Bitmap aBitmap(createMaskBmp(pA->GetBitmap(), pA->GetColor())); - createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + createBitmapPrimitive(aBitmap, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current()); break; } @@ -2111,8 +2026,8 @@ namespace wmfemfhelper aCroppedBitmap.Crop(aCropRectangle); } - const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor())); - createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); + const Bitmap aCroppedBitmap2(createMaskBmp(aCroppedBitmap, pA->GetColor())); + createBitmapPrimitive(aCroppedBitmap2, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current()); } break; @@ -2130,10 +2045,11 @@ namespace wmfemfhelper if(!aRange.isEmpty()) { const Gradient& rGradient = pA->GetGradient(); - const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(aRange)); + basegfx::BColor aSingleColor; - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if (aAttribute.getColorStops().isSingleColor(aSingleColor)) { // not really a gradient. Create filled rectangle createFillPrimitive( @@ -2162,7 +2078,7 @@ namespace wmfemfhelper xGradient[0] = drawinglayer::primitive2d::Primitive2DReference( new drawinglayer::primitive2d::FillGradientPrimitive2D( aRange, - aAttribute)); + std::move(aAttribute))); } // #i112300# clip against polygon representing the rectangle from @@ -2170,9 +2086,9 @@ namespace wmfemfhelper // when a MetaGradientAction is executed aOutline.transform(rPropertyHolders.Current().getTransformation()); rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::MaskPrimitive2D>( - aOutline, - xGradient)); + new drawinglayer::primitive2d::MaskPrimitive2D( + std::move(aOutline), + std::move(xGradient))); } } } @@ -2188,7 +2104,7 @@ namespace wmfemfhelper if(aOutline.count()) { const Hatch& rHatch = pA->GetHatch(); - const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch)); + drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch)); aOutline.transform(rPropertyHolders.Current().getTransformation()); @@ -2197,11 +2113,11 @@ namespace wmfemfhelper new drawinglayer::primitive2d::FillHatchPrimitive2D( aObjectRange, basegfx::BColor(), - aAttribute)); + std::move(aAttribute))); rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::MaskPrimitive2D>( - aOutline, + new drawinglayer::primitive2d::MaskPrimitive2D( + std::move(aOutline), drawinglayer::primitive2d::Primitive2DContainer { aFillHatch })); } @@ -2241,7 +2157,7 @@ namespace wmfemfhelper rWallpaper.GetGradient(), rPropertyHolders.Current())); } - else if(!rWallpaper.GetColor().GetTransparency()) + else if(!rWallpaper.GetColor().IsTransparent()) { // create color background rTargetHolders.Current().append( @@ -2421,14 +2337,12 @@ namespace wmfemfhelper // prepare translation, add current transformation basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove()); aVector *= rPropertyHolders.Current().getTransformation(); - basegfx::B2DHomMatrix aTransform( - basegfx::utils::createTranslateB2DHomMatrix(aVector)); // transform existing region basegfx::B2DPolyPolygon aClipPolyPolygon( rPropertyHolders.Current().getClipPolyPolygon()); - aClipPolyPolygon.transform(aTransform); + aClipPolyPolygon.translate(aVector); HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders); } } @@ -2528,34 +2442,15 @@ namespace wmfemfhelper } else { - switch(rMapMode.GetMapUnit()) + const auto eFrom = MapToO3tlLength(rPropertyHolders.Current().getMapUnit()), + eTo = MapToO3tlLength(rMapMode.GetMapUnit()); + if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) { - case MapUnit::Map100thMM : - { - if(MapUnit::MapTwip == rPropertyHolders.Current().getMapUnit()) - { - // MapUnit::MapTwip -> MapUnit::Map100thMM - const double fTwipTo100thMm(127.0 / 72.0); - aMapping.scale(fTwipTo100thMm, fTwipTo100thMm); - } - break; - } - case MapUnit::MapTwip : - { - if(MapUnit::Map100thMM == rPropertyHolders.Current().getMapUnit()) - { - // MapUnit::Map100thMM -> MapUnit::MapTwip - const double f100thMmToTwip(72.0 / 127.0); - aMapping.scale(f100thMmToTwip, f100thMmToTwip); - } - break; - } - default : - { - OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)"); - break; - } + const double fConvert(o3tl::convert(1.0, eFrom, eTo)); + aMapping.scale(fConvert, fConvert); } + else + OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)"); aMapping = getTransformFromMapMode(rMapMode) * aMapping; rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit()); @@ -2633,8 +2528,8 @@ namespace wmfemfhelper case MetaActionType::POP : { /** CHECKED, WORKS WELL */ - const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::CLIPREGION); - const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PushFlags::RASTEROP); + const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::CLIPREGION); + const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & vcl::PushFlags::RASTEROP); if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive()) { @@ -2703,7 +2598,7 @@ namespace wmfemfhelper // create primitives there and get them createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current()); - const drawinglayer::primitive2d::Primitive2DContainer aSubContent( + drawinglayer::primitive2d::Primitive2DContainer aSubContent( rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current())); // back to old target @@ -2712,8 +2607,8 @@ namespace wmfemfhelper if(!aSubContent.empty()) { rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( - aSubContent, + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + std::move(aSubContent), nTransparence * 0.01)); } } @@ -2746,7 +2641,7 @@ namespace wmfemfhelper // embed using EpsPrimitive rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::EpsPrimitive2D>( + new drawinglayer::primitive2d::EpsPrimitive2D( aObjectTransform, pA->GetLink(), pA->GetSubstitute())); @@ -2786,7 +2681,7 @@ namespace wmfemfhelper // Font. const MetaTextLineAction* pA = static_cast<const MetaTextLineAction*>(pAction); - proccessMetaTextLineAction( + processMetaTextLineAction( *pA, rTargetHolders.Current(), rPropertyHolders.Current()); @@ -2853,22 +2748,23 @@ namespace wmfemfhelper const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform( new drawinglayer::primitive2d::TransformPrimitive2D( aSubTransform, - xSubContent)); + std::move(xSubContent))); xSubContent = drawinglayer::primitive2d::Primitive2DContainer { aEmbeddedTransform }; } // check if gradient is a real gradient const Gradient& rGradient = pA->GetGradient(); - const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + basegfx::BColor aSingleColor; - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if (aAttribute.getColorStops().isSingleColor(aSingleColor)) { // not really a gradient; create UnifiedTransparencePrimitive2D rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( - xSubContent, - aAttribute.getStartColor().luminance())); + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + std::move(xSubContent), + aSingleColor.luminance())); } else { @@ -2880,12 +2776,12 @@ namespace wmfemfhelper const drawinglayer::primitive2d::Primitive2DReference xTransparence( new drawinglayer::primitive2d::FillGradientPrimitive2D( aRange, - aAttribute)); + std::move(aAttribute))); // create transparence primitive rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::TransparencePrimitive2D>( - xSubContent, + new drawinglayer::primitive2d::TransparencePrimitive2D( + std::move(xSubContent), drawinglayer::primitive2d::Primitive2DContainer { xTransparence })); } } @@ -2980,23 +2876,24 @@ namespace wmfemfhelper // get and check if gradient is a real gradient const Gradient& rGradient = pMetaGradientExAction->GetGradient(); - const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient)); + basegfx::BColor aSingleColor; - if(aAttribute.getStartColor() == aAttribute.getEndColor()) + if (aAttribute.getColorStops().isSingleColor(aSingleColor)) { // not really a gradient rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( - aPolyPolygon, - aAttribute.getStartColor())); + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + std::move(aPolyPolygon), + aSingleColor)); } else { // really a gradient rTargetHolders.Current().append( - std::make_unique<drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D>( + new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D( aPolyPolygon, - aAttribute)); + std::move(aAttribute))); } } } @@ -3006,11 +2903,11 @@ namespace wmfemfhelper if (aEMFPlus) { // error: should not yet exist - SAL_INFO("drawinglayer", "Error: multiple EMF_PLUS_HEADER_INFO"); + SAL_INFO("drawinglayer.emf", "Error: multiple EMF_PLUS_HEADER_INFO"); } else { - SAL_INFO("drawinglayer", "EMF+ passed to canvas mtf renderer - header info, size: " << pA->GetDataSize()); + SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pA->GetDataSize()); SvMemoryStream aMemoryStream(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(), StreamMode::READ); aEMFPlus.reset( @@ -3025,7 +2922,7 @@ namespace wmfemfhelper if (!aEMFPlus) { // error: should exist - SAL_INFO("drawinglayer", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO"); + SAL_INFO("drawinglayer.emf", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO"); } else { @@ -3038,11 +2935,11 @@ namespace wmfemfhelper if (char *env = getenv("EMF_PLUS_LIMIT")) { limit = atoi(env); - SAL_INFO("drawinglayer", "EMF+ records limit: " << limit); + SAL_INFO("drawinglayer.emf", "EMF+ records limit: " << limit); } } - SAL_INFO("drawinglayer", "EMF+ passed to canvas mtf renderer, size: " << pA->GetDataSize()); + SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer, size: " << pA->GetDataSize()); if (count < limit) { |