summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-01 15:40:00 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-07 15:08:59 +0200
commit3665e5b65d74d4ae22b637502efdac65d4b6c96c (patch)
tree6860d5553a8dd0ed374780b2690252d51c9a0bb9
parent96b3f84a2b1e3745326d54bc1ebe5eb69d536be5 (diff)
tdf#82214 optimize performance for primitives
See svg bug doc, which is processed quite slowly. Beyond needing faster renderers, there is also demand to improve the handling of primitives created by SVG import. Conflicts: drawinglayer/source/primitive2d/patternfillprimitive2d.cxx vcl/win/gdi/gdiimpl.cxx Change-Id: I10992a5746b8b2d6b50e3ee3fe415a035685c9ba
-rw-r--r--basegfx/source/polygon/b2dlinegeometry.cxx8
-rw-r--r--drawinglayer/source/primitive2d/patternfillprimitive2d.cxx205
-rw-r--r--drawinglayer/source/processor2d/objectinfoextractor2d.cxx20
-rw-r--r--drawinglayer/source/processor2d/vclpixelprocessor2d.cxx10
-rw-r--r--drawinglayer/source/texture/texture.cxx53
-rw-r--r--include/drawinglayer/primitive2d/patternfillprimitive2d.hxx20
-rw-r--r--include/drawinglayer/texture/texture.hxx6
-rw-r--r--vcl/win/gdi/gdiimpl.cxx125
8 files changed, 362 insertions, 85 deletions
diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx
index b5163ec1c957..abf80ccc59ce 100644
--- a/basegfx/source/polygon/b2dlinegeometry.cxx
+++ b/basegfx/source/polygon/b2dlinegeometry.cxx
@@ -955,6 +955,14 @@ namespace basegfx
}
}
}
+ else
+ {
+ // point count, but no edge count -> single point
+ aRetval.append(
+ createPolygonFromCircle(
+ aCandidate.getB2DPoint(0),
+ fHalfLineWidth));
+ }
return aRetval;
}
diff --git a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
index ff3e4452886b..39b695c4be7a 100644
--- a/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/patternfillprimitive2d.cxx
@@ -21,19 +21,144 @@
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <drawinglayer/texture/texture.hxx>
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
-
+#include <drawinglayer/tools/converters.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
using namespace com::sun::star;
+#define MAXIMUM_SQUARE_LENGTH (164.0)
+#define MINIMUM_SQUARE_LENGTH (32.0)
+#define MINIMUM_TILES_LENGTH (3)
namespace drawinglayer
{
namespace primitive2d
{
+ void PatternFillPrimitive2D::calculateNeededDiscreteBufferSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // reset parameters
+ rWidth = rHeight = 0;
+
+ // check if resolution is in the range which may be buffered
+ const basegfx::B2DPolyPolygon& rMaskPolygon = getMask();
+ const basegfx::B2DRange aMaskRange(rMaskPolygon.getB2DRange());
+
+ // get discrete rounded up square size of a single tile
+ const basegfx::B2DHomMatrix aMaskRangeTransformation(
+ basegfx::tools::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());
+ const double fSquare(fW * fH);
+
+ if(fSquare > 0.0)
+ {
+ // check if less than a maximum square pixels is used
+ static sal_uInt32 fMaximumSquare(MAXIMUM_SQUARE_LENGTH * MAXIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMaximumSquare)
+ {
+ // calculate needed number of tiles and check if used more than a minimum count
+ const texture::GeoTexSvxTiled aTiling(getReferenceRange());
+ const sal_uInt32 nTiles(aTiling.getNumberOfTiles());
+ static sal_uInt32 nMinimumTiles(MINIMUM_TILES_LENGTH * MINIMUM_TILES_LENGTH);
+
+ if(nTiles >= nMinimumTiles)
+ {
+ rWidth = basegfx::fround(ceil(fW));
+ rHeight = basegfx::fround(ceil(fH));
+ static sal_uInt32 fMinimumSquare(MINIMUM_SQUARE_LENGTH * MINIMUM_SQUARE_LENGTH);
+
+ if(fSquare < fMinimumSquare)
+ {
+ const double fRel(fW/fH);
+ rWidth = basegfx::fround(sqrt(fMinimumSquare * fRel));
+ rHeight = basegfx::fround(sqrt(fMinimumSquare / fRel));
+ }
+ }
+ }
+ }
+ }
+
+ Primitive2DContainer PatternFillPrimitive2D::createContent(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ Primitive2DContainer aContent;
+
+ // see if buffering is wanted. it is wanted, create buffered content in given resolution
+ if(0 != mnDiscreteWidth && 0 != mnDiscreteHeight)
+ {
+ const geometry::ViewInformation2D aViewInformation2D;
+ const primitive2d::Primitive2DReference xEmbedRef(
+ new primitive2d::TransformPrimitive2D(
+ basegfx::tools::createScaleB2DHomMatrix(mnDiscreteWidth, mnDiscreteHeight),
+ getChildren()));
+ const primitive2d::Primitive2DContainer xEmbedSeq { xEmbedRef };
+
+ const BitmapEx aBitmapEx(
+ tools::convertToBitmapEx(
+ xEmbedSeq,
+ aViewInformation2D,
+ mnDiscreteWidth,
+ mnDiscreteHeight,
+ mnDiscreteWidth * mnDiscreteHeight));
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ const Size& rBmpPix = aBitmapEx.GetSizePixel();
+
+ if(rBmpPix.Width() > 0 && rBmpPix.Height() > 0)
+ {
+ const primitive2d::Primitive2DReference xEmbedRefBitmap(
+ new primitive2d::BitmapPrimitive2D(
+ aBitmapEx,
+ basegfx::B2DHomMatrix()));
+ aContent = primitive2d::Primitive2DContainer { xEmbedRefBitmap };
+ }
+ }
+ }
+
+ if(aContent.empty())
+ {
+ // buffering was not tried or did fail - reset remembered buffered size
+ // in any case
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = pThat->mnDiscreteHeight = 0;
+
+ // use children as default context
+ aContent = getChildren();
+
+ // 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));
+
+ if(!aUnitRange.isInside(aContentRange))
+ {
+ const Primitive2DReference xRef(
+ new MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aUnitRange)),
+ aContent));
+
+ aContent = Primitive2DContainer { xRef };
+ }
+ }
+
+ return aContent;
+ }
+
Primitive2DContainer PatternFillPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
{
Primitive2DContainer aRetval;
@@ -52,20 +177,8 @@ namespace drawinglayer
aTiling.appendTransformations(aMatrices);
- // 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));
- Primitive2DContainer aContent(getChildren());
-
- if(!aUnitRange.isInside(aContentRange))
- {
- const Primitive2DReference xRef(
- new MaskPrimitive2D(
- basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(aUnitRange)),
- aContent));
-
- aContent = Primitive2DContainer { xRef };
- }
+ // create content
+ const Primitive2DContainer aContent(createContent(rViewInformation));
// resize result
aRetval.resize(aMatrices.size());
@@ -117,7 +230,9 @@ namespace drawinglayer
: BufferedDecompositionPrimitive2D(),
maMask(rMask),
maChildren(rChildren),
- maReferenceRange(rReferenceRange)
+ maReferenceRange(rReferenceRange),
+ mnDiscreteWidth(0),
+ mnDiscreteHeight(0)
{
}
@@ -140,6 +255,64 @@ namespace drawinglayer
return getMask().getB2DRange();
}
+ Primitive2DContainer PatternFillPrimitive2D::get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(0 == mnDiscreteWidth || 0 == mnDiscreteHeight)
+ {
+ // Currently no buffering is used. Check if the resulting discrete sizes
+ // in the current situation would be good for buffering from now on
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+
+ calculateNeededDiscreteBufferSize(pThat->mnDiscreteWidth, pThat->mnDiscreteHeight, rViewInformation);
+ }
+ else
+ {
+ // The existing bufferd decomposition uses a buffer in the remembered
+ // size. Get new needed sizes which depend on the given ViewInformation
+ sal_uInt32 nW(0);
+ sal_uInt32 nH(0);
+ calculateNeededDiscreteBufferSize(nW, nH, rViewInformation);
+
+ if(0 != nW && 0 != nH)
+ {
+ // buffering is possible - check if reset is needed
+ bool bResetBuffering = false;
+
+ if(nW > mnDiscreteWidth || nH > mnDiscreteHeight)
+ {
+ // Higher resolution is needed than used in the existing buffered
+ // decomposition
+ bResetBuffering = true;
+ }
+ else if(double(nW * nH) / double(mnDiscreteWidth * mnDiscreteHeight) <= 0.5)
+ {
+ // Size has shrunk for 50% or more - it's worth to refresh the buffering
+ bResetBuffering = true;
+ }
+
+ if(bResetBuffering)
+ {
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = nW;
+ pThat->mnDiscreteHeight = nH;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+ else
+ {
+ // no buffering wanted or possible - clear decomposition to create a
+ // new, unbuffered one
+ PatternFillPrimitive2D* pThat = const_cast< PatternFillPrimitive2D* >(this);
+ pThat->mnDiscreteWidth = 0;
+ pThat->mnDiscreteHeight = 0;
+ pThat->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ // call parent
+ return BufferedDecompositionPrimitive2D::get2DDecomposition(rViewInformation);
+ }
+
// provide unique ID
ImplPrimitive2DIDBlock(PatternFillPrimitive2D, PRIMITIVE2D_ID_PATTERNFILLPRIMITIVE2D)
diff --git a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
index ad26620a1630..7ae806f2890b 100644
--- a/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
+++ b/drawinglayer/source/processor2d/objectinfoextractor2d.cxx
@@ -40,8 +40,24 @@ namespace drawinglayer
}
default :
{
- // process recursively
- process(rCandidate.get2DDecomposition(getViewInformation2D()));
+ // we look for an encapsulated primitive, so do not decompose primitives
+ // based on GroupPrimitive2D, just visit their children. It may be that more
+ // group-like primitives need to be added here, but all primitives with
+ // grouping functionality should be implemented based on the GroupPrimitive2D
+ // class and have their main content accessible as children
+ const primitive2d::GroupPrimitive2D* pGroupPrimitive2D = dynamic_cast< const primitive2d::GroupPrimitive2D* >(&rCandidate);
+
+ if(pGroupPrimitive2D)
+ {
+ // process group children recursively
+ process(pGroupPrimitive2D->getChildren());
+ }
+ else
+ {
+ // do not process recursively, we *only* want to find existing
+ // ObjectInfoPrimitive2D entries
+ }
+
break;
}
}
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 2e6bc257b337..1424d8a1f182 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -182,17 +182,19 @@ namespace drawinglayer
bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency)
{
- basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
-
- if(!aLocalPolygon.count())
+ if(!rSource.getB2DPolygon().count())
{
// no geometry, done
return true;
}
- aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
+ // get geometry data, prepare hairline data
+ basegfx::B2DPolygon aLocalPolygon(rSource.getB2DPolygon());
basegfx::B2DPolyPolygon aHairLinePolyPolygon;
+ // simplify curve segments
+ aLocalPolygon = basegfx::tools::simplifyCurveSegments(aLocalPolygon);
+
if(rSource.getStrokeAttribute().isDefault() || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen())
{
// no line dashing, just copy
diff --git a/drawinglayer/source/texture/texture.cxx b/drawinglayer/source/texture/texture.cxx
index e205d89c53da..7d53a46e4d29 100644
--- a/drawinglayer/source/texture/texture.cxx
+++ b/drawinglayer/source/texture/texture.cxx
@@ -720,9 +720,20 @@ namespace drawinglayer
&& mfOffsetY == pCompare->mfOffsetY);
}
- void GeoTexSvxTiled::appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices)
+ sal_uInt32 GeoTexSvxTiled::getNumberOfTiles() const
+ {
+ return iterateTiles(nullptr);
+ }
+
+ void GeoTexSvxTiled::appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices) const
+ {
+ iterateTiles(&rMatrices);
+ }
+
+ sal_Int32 GeoTexSvxTiled::iterateTiles(::std::vector< basegfx::B2DHomMatrix >* pMatrices) const
{
const double fWidth(maRange.getWidth());
+ sal_Int32 nTiles = 0;
if(!basegfx::fTools::equalZero(fWidth))
{
@@ -774,12 +785,19 @@ namespace drawinglayer
for(double fPosY((nPosX % 2) ? fStartY - fHeight + (mfOffsetY * fHeight) : fStartY);
basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight)
{
- rMatrices.push_back(
- basegfx::tools::createScaleTranslateB2DHomMatrix(
- fWidth,
- fHeight,
- fPosX,
- fPosY));
+ if(pMatrices)
+ {
+ pMatrices->push_back(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ fWidth,
+ fHeight,
+ fPosX,
+ fPosY));
+ }
+ else
+ {
+ nTiles++;
+ }
}
}
}
@@ -790,17 +808,26 @@ namespace drawinglayer
for(double fPosX((nPosY % 2) ? fStartX - fWidth + (mfOffsetX * fWidth) : fStartX);
basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth)
{
- rMatrices.push_back(
- basegfx::tools::createScaleTranslateB2DHomMatrix(
- fWidth,
- fHeight,
- fPosX,
- fPosY));
+ if(pMatrices)
+ {
+ pMatrices->push_back(
+ basegfx::tools::createScaleTranslateB2DHomMatrix(
+ fWidth,
+ fHeight,
+ fPosX,
+ fPosY));
+ }
+ else
+ {
+ nTiles++;
+ }
}
}
}
}
}
+
+ return nTiles;
}
} // end of namespace texture
} // end of namespace drawinglayer
diff --git a/include/drawinglayer/primitive2d/patternfillprimitive2d.hxx b/include/drawinglayer/primitive2d/patternfillprimitive2d.hxx
index bdd8bbe35d4a..8e10564251b8 100644
--- a/include/drawinglayer/primitive2d/patternfillprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/patternfillprimitive2d.hxx
@@ -42,9 +42,24 @@ namespace drawinglayer
{
private:
const basegfx::B2DPolyPolygon maMask;
- const Primitive2DContainer maChildren;
+ const Primitive2DContainer maChildren;
const basegfx::B2DRange maReferenceRange;
+ /// values holding the discrete buffer size
+ sal_uInt32 mnDiscreteWidth;
+ sal_uInt32 mnDiscreteHeight;
+
+ /// helper that is capable to calculate the needed discrete buffer size for
+ /// eventually buffered content
+ void calculateNeededDiscreteBufferSize(
+ sal_uInt32& rWidth,
+ sal_uInt32& rHeight,
+ const geometry::ViewInformation2D& rViewInformation) const;
+
+ /// helper which creates the content - checks if clipping is needed and eventually
+ /// creates buffered content to speed up rendering
+ Primitive2DContainer createContent(const geometry::ViewInformation2D& rViewInformation) const;
+
protected:
/// create local decomposition
virtual Primitive2DContainer create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override;
@@ -67,6 +82,9 @@ namespace drawinglayer
/// get range
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
+ /// overload to react on evtl. buffered content
+ virtual Primitive2DContainer get2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override;
+
/// provide unique ID
DeclPrimitive2DIDBlock()
};
diff --git a/include/drawinglayer/texture/texture.hxx b/include/drawinglayer/texture/texture.hxx
index bbe620433a00..b4d78ba7a635 100644
--- a/include/drawinglayer/texture/texture.hxx
+++ b/include/drawinglayer/texture/texture.hxx
@@ -324,6 +324,9 @@ namespace drawinglayer
double mfOffsetX;
double mfOffsetY;
+ private:
+ sal_Int32 iterateTiles(::std::vector< basegfx::B2DHomMatrix >* pMatrices) const;
+
public:
GeoTexSvxTiled(
const basegfx::B2DRange& rRange,
@@ -334,7 +337,8 @@ namespace drawinglayer
// compare operator
virtual bool operator==(const GeoTexSvx& rGeoTexSvx) const override;
- void appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices);
+ void appendTransformations(::std::vector< basegfx::B2DHomMatrix >& rMatrices) const;
+ sal_uInt32 getNumberOfTiles() const;
};
} // end of namespace texture
} // end of namespace drawinglayer
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
index c5e591a2eaf7..3728755cb756 100644
--- a/vcl/win/gdi/gdiimpl.cxx
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -1896,9 +1896,10 @@ bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt
}
void impAddB2DPolygonToGDIPlusGraphicsPathReal(
- Gdiplus::GraphicsPath& rGraphicsPath,
+ Gdiplus::GpPath *pPath,
const basegfx::B2DPolygon& rPolygon,
- bool bNoLineJoin)
+ bool bNoLineJoin,
+ const basegfx::B2DVector* pLineWidths)
{
sal_uInt32 nCount(rPolygon.count());
@@ -1908,56 +1909,85 @@ void impAddB2DPolygonToGDIPlusGraphicsPathReal(
const bool bControls(rPolygon.areControlPointsUsed());
basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
- for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ if(nEdgeCount)
{
- const sal_uInt32 nNextIndex((a + 1) % nCount);
- const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
- const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
- const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
-
- if(b1stControlPointUsed || b2ndControlPointUsed)
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
{
- basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
- basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
-
- // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
- // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
- // no 1st or 2nd control point, despite that these are mathematicaly correct definitions
- // (basegfx can handle that). To solve, create replacement vectors to thre resp. next
- // control point with 1/3rd of length (the default control vector for these cases).
- // Only one of this can happen here, else the is(Next|Prev)ControlPointUsed wopuld have
- // both been false.
- // Caution: This error (and it's correction) might be necessary for other graphical
- // sub-systems in a similar way
- if(!b1stControlPointUsed)
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
+ const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
+
+ if(b1stControlPointUsed || b2ndControlPointUsed)
{
- aCa = aCurr + ((aCb - aCurr) * 0.3);
+ basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
+ basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
+
+ // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
+ // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
+ // no 1st or 2nd control point, despite that these are mathematicaly correct definitions
+ // (basegfx can handle that). To solve, create replacement vectors to thre resp. next
+ // control point with 1/3rd of length (the default control vector for these cases).
+ // Only one of this can happen here, else the is(Next|Prev)ControlPointUsed wopuld have
+ // both been false.
+ // Caution: This error (and it's correction) might be necessary for other graphical
+ // sub-systems in a similar way
+ if(!b1stControlPointUsed)
+ {
+ aCa = aCurr + ((aCb - aCurr) * 0.3);
+ }
+ else if(!b2ndControlPointUsed)
+ {
+ aCb = aNext + ((aCa - aNext) * 0.3);
+ }
+
+ Gdiplus::DllExports::GdipAddPathBezier(
+ pPath,
+ aCurr.getX(), aCurr.getY(),
+ aCa.getX(), aCa.getY(),
+ aCb.getX(), aCb.getY(),
+ aNext.getX(), aNext.getY());
}
- else if(!b2ndControlPointUsed)
+ else
{
- aCb = aNext + ((aCa - aNext) * 0.3);
+ if(pLineWidths && aCurr.equal(aNext))
+ {
+ // For lines with no length Gdiplus unfortunately paints nothing,
+ // independent of LineCaps being set. This differs from e.g. SVG
+ // and other systems. To get geometry created, add some offset,
+ // based on line width to have something relative to current metrics
+ if(!basegfx::fTools::equalZero(pLineWidths->getX()))
+ {
+ Gdiplus::DllExports::GdipAddPathLine(
+ pPath,
+ aCurr.getX(), aCurr.getY(),
+ aNext.getX() + (pLineWidths->getX() * 0.1), aNext.getY());
+ }
+ else
+ {
+ Gdiplus::DllExports::GdipAddPathLine(
+ pPath,
+ aCurr.getX(), aCurr.getY(),
+ aNext.getX(), aNext.getY() + (pLineWidths->getY() * 0.1));
+ }
+ }
+ else
+ {
+ Gdiplus::DllExports::GdipAddPathLine(
+ pPath,
+ aCurr.getX(), aCurr.getY(),
+ aNext.getX(), aNext.getY());
+ }
}
- rGraphicsPath.AddBezier(
- static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
- static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
- static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
- static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
- }
- else
- {
- rGraphicsPath.AddLine(
- static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
- static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
- }
-
- if(a + 1 < nEdgeCount)
- {
- aCurr = aNext;
-
- if(bNoLineJoin)
+ if(a + 1 < nEdgeCount)
{
- rGraphicsPath.StartFigure();
+ aCurr = aNext;
+
+ if(bNoLineJoin)
+ {
+ Gdiplus::DllExports::GdipStartPathFigure(pPath);
+ }
}
}
}
@@ -1984,9 +2014,8 @@ bool WinSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPo
aGraphicsPath.StartFigure();
}
- impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolyPolygon.getB2DPolygon(a), false);
-
- aGraphicsPath.CloseFigure();
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(pPath, rPolyPolygon.getB2DPolygon(a), false, 0);
+ Gdiplus::DllExports::GdipClosePathFigure(pPath);
}
if(mrParent.getAntiAliasB2DDraw())
@@ -2098,7 +2127,7 @@ bool WinSalGraphicsImpl::drawPolyLine(
}
}
- impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolygon, bNoLineJoin);
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(pPath, rPolygon, bNoLineJoin, &rLineWidths);
if(rPolygon.isClosed() && !bNoLineJoin)
{