summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drawinglayer/Library_drawinglayer.mk1
-rw-r--r--drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx10
-rw-r--r--drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx3
-rwxr-xr-xdrawinglayer/source/processor2d/SDPRProcessor2dTools.cxx277
-rw-r--r--drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx159
-rw-r--r--include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx31
-rwxr-xr-xinclude/drawinglayer/processor2d/SDPRProcessor2dTools.hxx104
-rw-r--r--include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx3
8 files changed, 571 insertions, 17 deletions
diff --git a/drawinglayer/Library_drawinglayer.mk b/drawinglayer/Library_drawinglayer.mk
index 884c32b170a8..40abc8d0f0fa 100644
--- a/drawinglayer/Library_drawinglayer.mk
+++ b/drawinglayer/Library_drawinglayer.mk
@@ -66,6 +66,7 @@ endif
ifeq ($(OS),WNT)
$(eval $(call gb_Library_add_exception_objects,drawinglayer,\
drawinglayer/source/processor2d/d2dpixelprocessor2d \
+ drawinglayer/source/processor2d/SDPRProcessor2dTools \
))
endif
diff --git a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
index be58c77dd390..2eb1373df0ae 100644
--- a/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
+++ b/drawinglayer/source/drawinglayeruno/xprimitive2drenderer.cxx
@@ -116,7 +116,15 @@ namespace drawinglayer::unorenderer
MaximumQuadraticPixels = 500000;
}
- const auto aViewInformation2D = geometry::createViewInformation2D(aViewInformationSequence);
+ 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));
diff --git a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
index 383d199f52b0..8c50993e6033 100644
--- a/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/fillgraphicprimitive2d.cxx
@@ -95,7 +95,8 @@ namespace drawinglayer::primitive2d
basegfx::B2DHomMatrix aTransformation,
const attribute::FillGraphicAttribute& rFillGraphic)
: maTransformation(std::move(aTransformation)),
- maFillGraphic(rFillGraphic)
+ maFillGraphic(rFillGraphic),
+ maOffsetXYCreatedBitmap()
{
}
diff --git a/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx
new file mode 100755
index 000000000000..6133e7a99936
--- /dev/null
+++ b/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx
@@ -0,0 +1,277 @@
+/* -*- 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/bitmapex.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#include <vcl/filter/PngImageWriter.hxx>
+#endif
+
+namespace drawinglayer::processor2d
+{
+void setOffsetXYCreatedBitmap(
+ drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ const BitmapEx& rBitmap)
+{
+ rFillGraphicPrimitive2D.impSetOffsetXYCreatedBitmap(rBitmap);
+}
+
+void takeCareOfOffsetXY(
+ const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ BitmapEx& 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& rSize(rTarget.GetSizePixel());
+ const tools::Long w(rSize.Width());
+ const tools::Long a(basegfx::fround(w * (1.0 - rFillGraphicAttribute.getOffsetX())));
+
+ if (0 != a && w != a)
+ {
+ const tools::Long h(rSize.Height());
+ const tools::Long b(w - a);
+ BitmapEx aTarget(Size(w, h * 2), rTarget.getPixelFormat());
+
+ aTarget.SetPrefSize(
+ Size(rTarget.GetPrefSize().Width(), rTarget.GetPrefSize().Height() * 2));
+ const tools::Rectangle aSrcDst(Point(), rSize);
+ 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& rSize(rTarget.GetSizePixel());
+ const tools::Long h(rSize.Height());
+ const tools::Long a(basegfx::fround(h * (1.0 - rFillGraphicAttribute.getOffsetY())));
+
+ if (0 != a && h != a)
+ {
+ const tools::Long w(rSize.Width());
+ const tools::Long b(h - a);
+ BitmapEx aTarget(Size(w * 2, h), rTarget.getPixelFormat());
+
+ aTarget.SetPrefSize(
+ Size(rTarget.GetPrefSize().Width() * 2, rTarget.GetPrefSize().Height()));
+ const tools::Rectangle aSrcDst(Point(), rSize);
+ 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 geometry::ViewInformation2D& rViewInformation2D, BitmapEx& 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(0.0, 0.0, 1.0, 1.0);
+ aDiscreteRange.transform(aLocalTransform);
+
+ if (!rDiscreteViewPort.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.GetBitmapEx();
+ }
+ 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 BitmapEx 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.GetBitmapEx();
+ }
+ }
+
+ 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(
+ OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
+ 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;
+}
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
index 2f3d6e25dced..cedf276e0c3c 100644
--- a/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/d2dpixelprocessor2d.cxx
@@ -26,6 +26,7 @@
#include <postwin.h>
#include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx>
+#include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
#include <sal/log.hxx>
#include <vcl/outdev.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
@@ -1109,15 +1110,18 @@ void D2DPixelProcessor2D::processTransparencePrimitive2D(
basegfx::B2DRange aDiscreteRange(
rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation());
- const D2D1_SIZE_U aB2DSizePixel(getRenderTarget()->GetPixelSize());
- const basegfx::B2DRange aViewRange(0.0, 0.0, aB2DSizePixel.width, aB2DSizePixel.height);
+ const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
basegfx::B2DRange aVisibleRange(aDiscreteRange);
- aVisibleRange.intersect(aViewRange);
- if (aVisibleRange.isEmpty())
+ if (!rDiscreteViewPort.isEmpty())
{
- // not visible, done
- return;
+ aVisibleRange.intersect(rDiscreteViewPort);
+
+ if (aVisibleRange.isEmpty())
+ {
+ // not visible, done
+ return;
+ }
}
// try to create directly, this needs the current mpRT to be a ID2D1DeviceContext/d2d1_1
@@ -1204,12 +1208,11 @@ void D2DPixelProcessor2D::processUnifiedTransparencePrimitive2D(
basegfx::B2DRange aTransparencyRange(
rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
aTransparencyRange.transform(getViewInformation2D().getObjectToViewTransformation());
- const D2D1_SIZE_U aB2DSizePixel(getRenderTarget()->GetPixelSize());
- const basegfx::B2DRange aViewRange(0.0, 0.0, aB2DSizePixel.width, aB2DSizePixel.height);
+ const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
- // not visible, done
- if (!aViewRange.overlaps(aTransparencyRange))
+ if (!rDiscreteViewPort.isEmpty() && !rDiscreteViewPort.overlaps(aTransparencyRange))
{
+ // we have a Viewport and visible range of geometry is outside -> not visible, done
return;
}
@@ -1255,12 +1258,11 @@ void D2DPixelProcessor2D::processMaskPrimitive2DPixel(
basegfx::B2DRange aMaskRange(aMask.getB2DRange());
aMaskRange.transform(getViewInformation2D().getObjectToViewTransformation());
- const D2D1_SIZE_U aB2DSizePixel(getRenderTarget()->GetPixelSize());
- const basegfx::B2DRange aViewRange(0.0, 0.0, aB2DSizePixel.width, aB2DSizePixel.height);
+ const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport());
- if (!aViewRange.overlaps(aMaskRange))
+ if (!rDiscreteViewPort.isEmpty() && !rDiscreteViewPort.overlaps(aMaskRange))
{
- // not in visible range, done
+ // we have a Viewport and visible range of geometry is outside -> not visible, done
return;
}
@@ -1768,6 +1770,129 @@ void D2DPixelProcessor2D::processSingleLinePrimitive2D(
increaseError();
}
+void D2DPixelProcessor2D::processFillGraphicPrimitive2D(
+ const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D)
+{
+ BitmapEx aPreparedBitmap;
+ basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange());
+ static 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 (decompositon)
+ process(rFillGraphicPrimitive2D);
+ return;
+ }
+
+ // render tiled using the prepared Bitmap data
+ if (maBColorModifierStack.count())
+ {
+ // need to apply ColorModifier to Bitmap data
+ aPreparedBitmap = aPreparedBitmap.ModifyBitmapEx(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::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
if (0 == mnRecursionCounter)
@@ -1915,6 +2040,12 @@ void D2DPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate));
break;
}
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ {
+ processFillGraphicPrimitive2D(
+ static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate));
+ break;
+ }
// continue with decompose as fallback
default:
diff --git a/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx b/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx
index bf8222f23785..899851adc84e 100644
--- a/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/fillgraphicprimitive2d.hxx
@@ -24,10 +24,24 @@
#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <drawinglayer/attribute/fillgraphicattribute.hxx>
+#include <vcl/bitmapex.hxx>
+namespace drawinglayer::primitive2d
+{
+class FillGraphicPrimitive2D;
+}
-// FillbitmapPrimitive2D class
+namespace drawinglayer::processor2d
+{
+// define a simple accessor which can be used as friend. That method exists
+// only locally at SDPRProcessor2dTools.cxx and is thus only usable/callable
+// from there
+void setOffsetXYCreatedBitmap(
+ drawinglayer::primitive2d::FillGraphicPrimitive2D&,
+ const BitmapEx&);
+}
+// FillbitmapPrimitive2D class
namespace drawinglayer::primitive2d
{
/** FillGraphicPrimitive2D class
@@ -52,9 +66,23 @@ namespace drawinglayer::primitive2d
/// the fill attributes
attribute::FillGraphicAttribute maFillGraphic;
+ /// the evtl. buffered OffsetXYCreatedBitmap
+ BitmapEx maOffsetXYCreatedBitmap;
+
/// local decomposition.
virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override;
+ // allow this single acessor to change it to set buggered data
+ friend void drawinglayer::processor2d::setOffsetXYCreatedBitmap(
+ drawinglayer::primitive2d::FillGraphicPrimitive2D&,
+ const BitmapEx&);
+
+ // private tooling method to be called by setOffsetXYCreatedBitmap
+ void impSetOffsetXYCreatedBitmap(const BitmapEx& rBitmap)
+ {
+ maOffsetXYCreatedBitmap = rBitmap;
+ }
+
public:
/// constructor
FillGraphicPrimitive2D(
@@ -64,6 +92,7 @@ namespace drawinglayer::primitive2d
/// data read access
const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; }
const attribute::FillGraphicAttribute& getFillGraphic() const { return maFillGraphic; }
+ const BitmapEx& getOffsetXYCreatedBitmap() const { return maOffsetXYCreatedBitmap; }
/// compare operator
virtual bool operator==( const BasePrimitive2D& rPrimitive ) const override;
diff --git a/include/drawinglayer/processor2d/SDPRProcessor2dTools.hxx b/include/drawinglayer/processor2d/SDPRProcessor2dTools.hxx
new file mode 100755
index 000000000000..82791ac3eb4e
--- /dev/null
+++ b/include/drawinglayer/processor2d/SDPRProcessor2dTools.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+namespace drawinglayer::primitive2d
+{
+class FillGraphicPrimitive2D;
+}
+
+namespace geometry
+{
+class ViewInformation2D;
+}
+
+namespace basegfx
+{
+class B2DRange;
+}
+
+class BitmapEx;
+
+namespace drawinglayer::processor2d
+{
+/** helper to process FillGraphicPrimitive2D:
+
+ In places that want to implement direct rendering of this primitive
+ e.g. in SDPRs all impls would need to handle the FillGraphicAttribute
+ settings and the type of Graphic. Unify this by this helper in one place
+ since this may get complicated (many cases to cover).
+ It will create and return a BitmapEx when direct tiled rendering is
+ preferrable and suggested.
+ Of course every impl may still do what it wants, this is just to make
+ implementations easier.
+
+ @param rFillGraphicPrimitive2D
+ The primitive to work on
+
+ @param rViewInformation2D
+ The ViewInformation to work with (from the processor)
+
+ @param rTarget
+ The prepared PixelData to use for tiled rendering. If this
+ is empty on return this means to evtl. use the decompose.
+ Please hand in an empty one to make this work.
+
+ @param rFillUnitRange
+ This is a modifyable copy of FillGraphicAttribute.getGraphicRange(). We
+ need a modifyable one since params since OffsetX/OffsetY in
+ FillGraphicAttribute may require to change/adapt this if used
+
+ @param fBigDiscreteArea
+ Defines starting with which number of square pixels a target is seen
+ to be painted 'big'
+
+ @return
+ false: rendering is not needed (invalid, outside, ...), done
+ true: rendering is needed
+ -> if rTarget is filled, use for tiled rendering
+ -> if not, use fallback (primitive decomposition)
+
+ For the various reasons/things that get checked/tested/done, please
+ see the implementation
+ */
+bool prepareBitmapForDirectRender(
+ const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ const geometry::ViewInformation2D& rViewInformation2D, BitmapEx& rTarget,
+ basegfx::B2DRange& rFillUnitRange, double fBigDiscreteArea = 300.0 * 300.0);
+
+/** helper to react/process if OffsetX/OffsetY of the FillGraphicAttribute is used.
+
+ This is old but hard to remove stuff that allows hor/ver offset when
+ tiled fill is used. To solve that, create pixel data that doubles
+ resp. in width/height and copies the off-setted version of the bitmap
+ information to the extra space, so rendering does not need to do that.
+
+ Since this doubles the geometry, an adaption of the used fill range
+ (here rFillUnitRange in unit coordinates) also needs to be adapted,
+ refer to usage.
+ */
+void takeCareOfOffsetXY(
+ const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
+ BitmapEx& rTarget, basegfx::B2DRange& rFillUnitRange);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx b/include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx
index 10f9a11cb296..8d1929a2f522 100644
--- a/include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx
@@ -44,6 +44,7 @@ class PolygonStrokePrimitive2D;
class LineRectanglePrimitive2D;
class FilledRectanglePrimitive2D;
class SingleLinePrimitive2D;
+class FillGraphicPrimitive2D;
}
struct ID2D1RenderTarget;
@@ -89,6 +90,8 @@ class DRAWINGLAYER_DLLPUBLIC D2DPixelProcessor2D : public BaseProcessor2D
const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D);
void
processSingleLinePrimitive2D(const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D);
+ void processFillGraphicPrimitive2D(
+ const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D);
// common helpers
sal::systools::COMReference<ID2D1Bitmap>