diff options
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> |