summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <alg@apache.org>2013-10-11 16:57:46 +0000
committerCaolán McNamara <caolanm@redhat.com>2013-10-11 20:26:34 +0100
commit66c803ebcb4bc164ca5af1cf752aeb645735f186 (patch)
tree9f659782a9199c300562c8e0e56f652fcab57e87
parentd61168908ced2e4bae32b8e0ae0a6eba6dbf37fc (diff)
Resolves: #i123379# various SVG marker corrections
(cherry picked from commit ff1eee61fc56b06e436735f5e2b133db56de3608) Conflicts: basegfx/inc/basegfx/polygon/b2dpolygontools.hxx Change-Id: I818657573a1e673e312702a4b45e7bb6394250d2
-rw-r--r--basegfx/source/polygon/b2dpolygontools.cxx82
-rw-r--r--include/basegfx/polygon/b2dpolygontools.hxx8
-rw-r--r--svgio/inc/svgio/svgreader/svgstyleattributes.hxx8
-rw-r--r--svgio/source/svgreader/svgstyleattributes.cxx269
4 files changed, 230 insertions, 137 deletions
diff --git a/basegfx/source/polygon/b2dpolygontools.cxx b/basegfx/source/polygon/b2dpolygontools.cxx
index de75b15f9efe..583fa3e778ee 100644
--- a/basegfx/source/polygon/b2dpolygontools.cxx
+++ b/basegfx/source/polygon/b2dpolygontools.cxx
@@ -3226,6 +3226,88 @@ namespace basegfx
return true;
}
+ B2DVector getTangentEnteringPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ B2DVector aRetval(0.0, 0.0);
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nIndex >= nCount)
+ {
+ // out of range
+ return aRetval;
+ }
+
+ // start immediately at prev point compared to nIndex
+ const bool bClosed(rCandidate.isClosed());
+ sal_uInt32 nPrev(bClosed ? (nIndex + nCount - 1) % nCount : nIndex ? nIndex - 1 : nIndex);
+
+ if(nPrev == nIndex)
+ {
+ // no previous, done
+ return aRetval;
+ }
+
+ B2DCubicBezier aSegment;
+
+ // go backward in the polygon; if closed, maximal back to start index (nIndex); if not closed,
+ // until zero. Use nIndex as stop criteria
+ while(nPrev != nIndex)
+ {
+ // get BezierSegment and tangent at the *end* of segment
+ rCandidate.getBezierSegment(nPrev, aSegment);
+ aRetval = aSegment.getTangent(1.0);
+
+ if(!aRetval.equalZero())
+ {
+ // if we have a tangent, return it
+ return aRetval;
+ }
+
+ // prepare index before checked one
+ nPrev = bClosed ? (nPrev + nCount - 1) % nCount : nPrev ? nPrev - 1 : nIndex;
+ }
+
+ return aRetval;
+ }
+
+ B2DVector getTangentLeavingPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex)
+ {
+ B2DVector aRetval(0.0, 0.0);
+ const sal_uInt32 nCount(rCandidate.count());
+
+ if(nIndex >= nCount)
+ {
+ // out of range
+ return aRetval;
+ }
+
+ // start at nIndex
+ const bool bClosed(rCandidate.isClosed());
+ sal_uInt32 nCurrent(nIndex);
+ B2DCubicBezier aSegment;
+
+ // go forward; if closed, do this until once around and back at start index (nIndex); if not
+ // closed, until last point (nCount - 1). Use nIndex as stop criteria
+ do
+ {
+ // get BezierSegment and tangent at the *beginning* of segment
+ rCandidate.getBezierSegment(nCurrent, aSegment);
+ aRetval = aSegment.getTangent(0.0);
+
+ if(!aRetval.equalZero())
+ {
+ // if we have a tangent, return it
+ return aRetval;
+ }
+
+ // prepare next index
+ nCurrent = bClosed ? (nCurrent + 1) % nCount : nCurrent + 1 < nCount ? nCurrent + 1 : nIndex;
+ }
+ while(nCurrent != nIndex);
+
+ return aRetval;
+ }
+
} // end of namespace tools
} // end of namespace basegfx
diff --git a/include/basegfx/polygon/b2dpolygontools.hxx b/include/basegfx/polygon/b2dpolygontools.hxx
index 3be763bd18ad..af5559eff6a7 100644
--- a/include/basegfx/polygon/b2dpolygontools.hxx
+++ b/include/basegfx/polygon/b2dpolygontools.hxx
@@ -439,6 +439,14 @@ namespace basegfx
*/
bool containsOnlyHorizontalAndVerticalEdges(const B2DPolygon& rCandidate);
+ /// get the tangent with which the given point is entered seen from the previous
+ /// polygon path data. Take into account all stuff like closed state, zero-length edges and others.
+ BASEGFX_DLLPUBLIC B2DVector getTangentEnteringPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
+ /// get the tangent with which the given point is left seen from the following
+ /// polygon path data. Take into account all stuff like closed state, zero-length edges and others.
+ BASEGFX_DLLPUBLIC B2DVector getTangentLeavingPoint(const B2DPolygon& rCandidate, sal_uInt32 nIndex);
+
} // end of namespace tools
} // end of namespace basegfx
diff --git a/svgio/inc/svgio/svgreader/svgstyleattributes.hxx b/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
index 13e37057af41..be8ddd4e64e1 100644
--- a/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
+++ b/svgio/inc/svgio/svgreader/svgstyleattributes.hxx
@@ -248,14 +248,6 @@ namespace svgio
basegfx::B2DHomMatrix& rMarkerTransform,
basegfx::B2DRange& rClipRange,
const SvgMarkerNode& rMarker) const;
- void add_singleMarker(
- drawinglayer::primitive2d::Primitive2DSequence& rTarget,
- const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
- const basegfx::B2DHomMatrix& rMarkerTransform,
- const basegfx::B2DRange& rClipRange,
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rCandidate,
- const sal_uInt32 nIndex) const;
void add_markers(
const basegfx::B2DPolyPolygon& rPath,
drawinglayer::primitive2d::Primitive2DSequence& rTarget) const;
diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx
index 67219e7060a8..958debdfe21c 100644
--- a/svgio/source/svgreader/svgstyleattributes.cxx
+++ b/svgio/source/svgreader/svgstyleattributes.cxx
@@ -737,59 +737,6 @@ namespace svgio
}
}
- double get_markerRotation(
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rPolygon,
- const sal_uInt32 nIndex)
- {
- double fAngle(0.0);
- const sal_uInt32 nPointCount(rPolygon.count());
-
- if(nPointCount)
- {
- if(rMarker.getOrientAuto())
- {
- const bool bPrev(rPolygon.isClosed() || nIndex > 0);
- basegfx::B2DCubicBezier aSegment;
- basegfx::B2DVector aPrev;
- basegfx::B2DVector aNext;
-
- if(bPrev)
- {
- rPolygon.getBezierSegment((nIndex - 1) % nPointCount, aSegment);
- aPrev = aSegment.getTangent(1.0);
- }
-
- const bool bNext(rPolygon.isClosed() || nIndex + 1 < nPointCount);
-
- if(bNext)
- {
- rPolygon.getBezierSegment(nIndex % nPointCount, aSegment);
- aNext = aSegment.getTangent(0.0);
- }
-
- if(bPrev && bNext)
- {
- fAngle = atan2(aPrev.getY() + aNext.getY(), aPrev.getX() + aNext.getX());
- }
- else if(bPrev)
- {
- fAngle = atan2(aPrev.getY(), aPrev.getX());
- }
- else if(bNext)
- {
- fAngle = atan2(aNext.getY(), aNext.getX());
- }
- }
- else
- {
- fAngle = rMarker.getAngle();
- }
- }
-
- return fAngle;
- }
-
bool SvgStyleAttributes::prepare_singleMarker(
drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
basegfx::B2DHomMatrix& rMarkerTransform,
@@ -880,49 +827,6 @@ namespace svgio
return false;
}
- void SvgStyleAttributes::add_singleMarker(
- drawinglayer::primitive2d::Primitive2DSequence& rTarget,
- const drawinglayer::primitive2d::Primitive2DSequence& rMarkerPrimitives,
- const basegfx::B2DHomMatrix& rMarkerTransform,
- const basegfx::B2DRange& rClipRange,
- const SvgMarkerNode& rMarker,
- const basegfx::B2DPolygon& rCandidate,
- const sal_uInt32 nIndex) const
- {
- const sal_uInt32 nPointCount(rCandidate.count());
-
- if(nPointCount)
- {
- // get and apply rotation
- basegfx::B2DHomMatrix aCombinedTransform(rMarkerTransform);
- aCombinedTransform.rotate(get_markerRotation(rMarker, rCandidate, nIndex));
-
- // get and apply target position
- const basegfx::B2DPoint aPoint(rCandidate.getB2DPoint(nIndex % nPointCount));
- aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
-
- // prepare marker
- drawinglayer::primitive2d::Primitive2DReference xMarker(
- new drawinglayer::primitive2d::TransformPrimitive2D(
- aCombinedTransform,
- rMarkerPrimitives));
-
- if(!rClipRange.isEmpty())
- {
- // marker needs to be clipped, it's bigger as the mapping
- basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(rClipRange));
-
- aClipPolygon.transform(aCombinedTransform);
- xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
- aClipPolygon,
- drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1));
- }
-
- // add marker
- drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker);
- }
- }
-
void SvgStyleAttributes::add_markers(
const basegfx::B2DPolyPolygon& rPath,
drawinglayer::primitive2d::Primitive2DSequence& rTarget) const
@@ -934,51 +838,158 @@ namespace svgio
if(pStart || pMid || pEnd)
{
- const sal_uInt32 nCount(rPath.count());
+ const sal_uInt32 nSubPathCount(rPath.count());
- for (sal_uInt32 a(0); a < nCount; a++)
+ if(nSubPathCount)
{
- const basegfx::B2DPolygon aCandidate(rPath.getB2DPolygon(a));
- const sal_uInt32 nPointCount(aCandidate.count());
+ // remember prepared marker; pStart, pMid and pEnd may all be equal when
+ // only 'marker' was used instead of 'marker-start', 'marker-mid' or 'marker-end',
+ // see 'case SVGTokenMarker' in this file; thus in this case only one common
+ // marker in primitive form will be prepared
+ const SvgMarkerNode* pPrepared = 0;
+
+ // values for the prepared marker, results of prepare_singleMarker
+ drawinglayer::primitive2d::Primitive2DSequence aPreparedMarkerPrimitives;
+ basegfx::B2DHomMatrix aPreparedMarkerTransform;
+ basegfx::B2DRange aPreparedMarkerClipRange;
- if(nPointCount)
+ for (sal_uInt32 a(0); a < nSubPathCount; a++)
{
- const sal_uInt32 nMarkerCount(aCandidate.isClosed() ? nPointCount + 1 : nPointCount);
- drawinglayer::primitive2d::Primitive2DSequence aMarkerPrimitives;
- basegfx::B2DHomMatrix aMarkerTransform;
- basegfx::B2DRange aClipRange;
- const SvgMarkerNode* pPrepared = 0;
+ // iterate over sub-paths
+ const basegfx::B2DPolygon aSubPolygonPath(rPath.getB2DPolygon(a));
+ const sal_uInt32 nSubPolygonPointCount(aSubPolygonPath.count());
+ const bool bSubPolygonPathIsClosed(aSubPolygonPath.isClosed());
- if(pStart && a==0)
+ if(nSubPolygonPointCount)
{
- if(prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pStart))
- {
- pPrepared = pStart;
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, 0);
- }
- }
+ // for each sub-path, create one marker per point (when closed, two markers
+ // need to pe created for the 1st point)
+ const sal_uInt32 nTargetMarkerCount(bSubPolygonPathIsClosed ? nSubPolygonPointCount + 1 : nSubPolygonPointCount);
- if(pMid)
- {
- if(pMid == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pMid))
+ for (sal_uInt32 b(0); b < nTargetMarkerCount; b++)
{
- pPrepared = pMid;
- const sal_uInt32 nFirstIndex(a==0 ? 1 : 0);
- const sal_uInt32 nLastIndex(a==nCount-1 ? nMarkerCount-1 : nMarkerCount);
+ const bool bIsFirstMarker(!a && !b);
+ const bool bIsLastMarker(nSubPathCount - 1 == a && nTargetMarkerCount - 1 == b);
+ const SvgMarkerNode* pNeeded = 0;
- for(sal_uInt32 b(nFirstIndex); b < nLastIndex; b++)
+ if(bIsFirstMarker)
{
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, b);
+ // 1st point in 1st sub-polygon, use pStart
+ pNeeded = pStart;
+ }
+ else if(bIsLastMarker)
+ {
+ // last point in last sub-polygon, use pEnd
+ pNeeded = pEnd;
+ }
+ else
+ {
+ // anything in-between, use pMid
+ pNeeded = pMid;
}
- }
- }
- if(pEnd && a==nCount-1)
- {
- if(pEnd == pPrepared || prepare_singleMarker(aMarkerPrimitives, aMarkerTransform, aClipRange, *pEnd))
- {
- pPrepared = pEnd;
- add_singleMarker(rTarget, aMarkerPrimitives, aMarkerTransform, aClipRange, *pPrepared, aCandidate, nMarkerCount - 1);
+ if(!pNeeded)
+ {
+ // no marker needs to be created for this point
+ continue;
+ }
+
+ if(pPrepared != pNeeded)
+ {
+ // if needed marker is not yet prepared, do it now
+ if(prepare_singleMarker(aPreparedMarkerPrimitives, aPreparedMarkerTransform, aPreparedMarkerClipRange, *pNeeded))
+ {
+ pPrepared = pNeeded;
+ }
+ else
+ {
+ // error: could not prepare given marker
+ OSL_ENSURE(false, "OOps, could not prepare given marker as primitives (!)");
+ pPrepared = 0;
+ continue;
+ }
+ }
+
+ // prepare complete transform
+ basegfx::B2DHomMatrix aCombinedTransform(aPreparedMarkerTransform);
+
+ // get rotation
+ if(pPrepared->getOrientAuto())
+ {
+ const sal_uInt32 nPointIndex(b % nSubPolygonPointCount);
+
+ // get entering and leaving tangents; this will search backward/froward
+ // in the polygon to find tangents unequal to zero, skipping empty edges
+ // see basegfx descriptions)
+ // Hint: Mozilla, Inkscape and others use only leaving tangent for start marker
+ // and entering tangent for end marker. To achieve this (if wanted) it is possibe
+ // to make the fetch of aEntering/aLeaving dependent on bIsFirstMarker/bIsLastMarker.
+ // This is not done here, see comment 14 in task #1232379#
+ // or http://www.w3.org/TR/SVG/painting.html#OrientAttribute
+ basegfx::B2DVector aEntering(
+ basegfx::tools::getTangentEnteringPoint(
+ aSubPolygonPath,
+ nPointIndex));
+ basegfx::B2DVector aLeaving(
+ basegfx::tools::getTangentLeavingPoint(
+ aSubPolygonPath,
+ nPointIndex));
+ const bool bEntering(!aEntering.equalZero());
+ const bool bLeaving(!aLeaving.equalZero());
+
+ if(bEntering || bLeaving)
+ {
+ basegfx::B2DVector aSum(0.0, 0.0);
+
+ if(bEntering)
+ {
+ aSum += aEntering.normalize();
+ }
+
+ if(bLeaving)
+ {
+ aSum += aLeaving.normalize();
+ }
+
+ if(!aSum.equalZero())
+ {
+ const double fAngle(atan2(aSum.getY(), aSum.getX()));
+
+ // apply rotation
+ aCombinedTransform.rotate(fAngle);
+ }
+ }
+ }
+ else
+ {
+ // apply rotation
+ aCombinedTransform.rotate(pPrepared->getAngle());
+ }
+
+ // get and apply target position
+ const basegfx::B2DPoint aPoint(aSubPolygonPath.getB2DPoint(b % nSubPolygonPointCount));
+
+ aCombinedTransform.translate(aPoint.getX(), aPoint.getY());
+
+ // prepare marker
+ drawinglayer::primitive2d::Primitive2DReference xMarker(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aCombinedTransform,
+ aPreparedMarkerPrimitives));
+
+ if(!aPreparedMarkerClipRange.isEmpty())
+ {
+ // marker needs to be clipped, it's bigger as the mapping
+ basegfx::B2DPolyPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aPreparedMarkerClipRange));
+
+ aClipPolygon.transform(aCombinedTransform);
+ xMarker = new drawinglayer::primitive2d::MaskPrimitive2D(
+ aClipPolygon,
+ drawinglayer::primitive2d::Primitive2DSequence(&xMarker, 1));
+ }
+
+ // add marker
+ drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xMarker);
}
}
}