[Libreoffice-commits] core.git: Branch 'feature/skia' - vcl/skia

Tomaž Vajngerl (via logerrit) logerrit at kemper.freedesktop.org
Wed Oct 23 07:36:33 UTC 2019


 vcl/skia/gdiimpl.cxx |  280 ++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 201 insertions(+), 79 deletions(-)

New commits:
commit 3ea586b2a2bda1a5eb72185dc417bd73b32ea6eb
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed Oct 23 08:46:43 2019 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Wed Oct 23 09:07:07 2019 +0200

    skia: implement direct PolyPolygon and PolyLine rendering
    
    Polygon, PolyPolygon and PolyLine rendering taking point array is
    diverted to the one taking basegfx::B2DPolyPolygon or B2DPolygon.
    
    PolyPolygon and PolyLine is implemented with Skia, with bezier
    rendering included. The one thing missing is taking the input
    matrix into account.
    
    Change-Id: Id675dddcef95d2279410f5987424c1de6863cbe6

diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index 07a59c9a7ef8..c9d5d05ea433 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -38,34 +38,79 @@
 namespace
 {
 // Create Skia Path from B2DPolygon
-// TODO - take bezier curves into account
 // TODO - use this for all Polygon / PolyPolygon needs
 void lclPolygonToPath(const basegfx::B2DPolygon& rPolygon, SkPath& rPath)
 {
     const sal_uInt32 nPointCount(rPolygon.count());
 
-    if (nPointCount == 0)
+    if (nPointCount <= 1)
         return;
 
     const bool bClosePath(rPolygon.isClosed());
+    const bool bHasCurves(rPolygon.areControlPointsUsed());
 
     bool bFirst = true;
 
-    for (sal_uInt32 nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++)
+    sal_uInt32 nCurrentIndex = 0;
+    sal_uInt32 nPreviousIndex = nPointCount - 1;
+
+    basegfx::B2DPoint aCurrentPoint;
+    basegfx::B2DPoint aPreviousPoint;
+
+    for (sal_uInt32 nIndex = 0; nIndex <= nPointCount; nIndex++)
     {
-        auto const& rPoint = rPolygon.getB2DPoint(nPointIndex);
+        nCurrentIndex = nIndex % nPointCount;
+        aCurrentPoint = rPolygon.getB2DPoint(nCurrentIndex);
+
         if (bFirst)
         {
-            rPath.moveTo(rPoint.getX(), rPoint.getY());
+            rPath.moveTo(aCurrentPoint.getX(), aCurrentPoint.getY());
             bFirst = false;
         }
+        else if (!bHasCurves)
+        {
+            rPath.lineTo(aCurrentPoint.getX(), aCurrentPoint.getY());
+        }
         else
         {
-            rPath.lineTo(rPoint.getX(), rPoint.getY());
+            basegfx::B2DPoint aPreviousControlPoint = rPolygon.getNextControlPoint(nPreviousIndex);
+            basegfx::B2DPoint aCurrentControlPoint = rPolygon.getPrevControlPoint(nCurrentIndex);
+
+            if (aPreviousControlPoint.equal(aPreviousPoint))
+            {
+                aPreviousControlPoint
+                    = aPreviousPoint + ((aPreviousControlPoint - aCurrentPoint) * 0.0005);
+            }
+
+            if (aCurrentControlPoint.equal(aCurrentPoint))
+            {
+                aCurrentControlPoint
+                    = aCurrentPoint + ((aCurrentControlPoint - aPreviousPoint) * 0.0005);
+            }
+            rPath.cubicTo(aPreviousControlPoint.getX(), aPreviousControlPoint.getY(),
+                          aCurrentControlPoint.getX(), aCurrentControlPoint.getY(),
+                          aCurrentPoint.getX(), aCurrentPoint.getY());
         }
+        aPreviousPoint = aCurrentPoint;
+        nPreviousIndex = nCurrentIndex;
     }
     if (bClosePath)
+    {
         rPath.close();
+    }
+}
+
+void lclPolyPolygonToPath(const basegfx::B2DPolyPolygon& rPolyPolygon, SkPath& rPath)
+{
+    const sal_uInt32 nPolygonCount(rPolyPolygon.count());
+
+    if (nPolygonCount == 0)
+        return;
+
+    for (const auto& rPolygon : rPolyPolygon)
+    {
+        lclPolygonToPath(rPolygon, rPath);
+    }
 }
 
 SkColor toSkColor(Color color)
@@ -74,6 +119,11 @@ SkColor toSkColor(Color color)
                           color.GetBlue());
 }
 
+SkColor toSkColorWithTransparency(Color aColor, double fTransparency)
+{
+    return SkColorSetA(toSkColor(aColor), 255 * (1.0 - fTransparency));
+}
+
 Color fromSkColor(SkColor color)
 {
     return Color(255 - SkColorGetA(color), SkColorGetR(color), SkColorGetG(color),
@@ -302,105 +352,177 @@ void SkiaSalGraphicsImpl::drawRect(long nX, long nY, long nWidth, long nHeight)
 
 void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry)
 {
-    if (mLineColor == SALCOLOR_NONE)
-        return;
-    preDraw();
-    std::vector<SkPoint> pointVector;
-    pointVector.reserve(nPoints);
-    for (sal_uInt32 i = 0; i < nPoints; ++i)
-        pointVector.emplace_back(SkPoint::Make(pPtAry[i].mnX, pPtAry[i].mnY));
-    SkPaint paint;
-    paint.setColor(toSkColor(mLineColor));
-    mSurface->getCanvas()->drawPoints(SkCanvas::kLines_PointMode, nPoints, pointVector.data(),
-                                      paint);
-    postDraw();
+    basegfx::B2DPolygon aPolygon;
+    aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+    for (sal_uInt32 i = 1; i < nPoints; ++i)
+        aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
+    aPolygon.setClosed(false);
+
+    drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, basegfx::B2DVector(1.0, 1.0),
+                 basegfx::B2DLineJoin::Miter, css::drawing::LineCap_BUTT,
+                 basegfx::deg2rad(15.0) /*default*/, false);
 }
 
 void SkiaSalGraphicsImpl::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
 {
-    if (mLineColor == SALCOLOR_NONE && mFillColor == SALCOLOR_NONE)
-        return;
-    preDraw();
-    std::vector<SkPoint> pointVector;
-    pointVector.reserve(nPoints);
-    for (sal_uInt32 i = 0; i < nPoints; ++i)
-        pointVector.emplace_back(SkPoint::Make(pPtAry[i].mnX, pPtAry[i].mnY));
-    SkPath path;
-    path.addPoly(pointVector.data(), nPoints, false);
-    SkPaint paint;
-    if (mFillColor != SALCOLOR_NONE)
-    {
-        paint.setColor(toSkColor(mFillColor));
-        paint.setStyle(SkPaint::kFill_Style);
-        mSurface->getCanvas()->drawPath(path, paint);
-    }
-    if (mLineColor != SALCOLOR_NONE)
-    {
-        paint.setColor(toSkColor(mLineColor));
-        paint.setStyle(SkPaint::kStroke_Style);
-        mSurface->getCanvas()->drawPath(path, paint);
-    }
-    postDraw();
+    basegfx::B2DPolygon aPolygon;
+    aPolygon.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+    for (sal_uInt32 i = 1; i < nPoints; ++i)
+        aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
+
+    drawPolyPolygon(basegfx::B2DHomMatrix(), basegfx::B2DPolyPolygon(aPolygon), 0.0);
 }
 
 void SkiaSalGraphicsImpl::drawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32* pPoints,
                                           PCONSTSALPOINT* pPtAry)
 {
-    if (mLineColor == SALCOLOR_NONE && mFillColor == SALCOLOR_NONE)
-        return;
-    preDraw();
-    std::vector<SkPoint> pointVector;
-    SkPath path;
-    for (sal_uInt32 poly = 0; poly < nPoly; ++poly)
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    for (sal_uInt32 nPolygon = 0; nPolygon < nPoly; ++nPolygon)
     {
-        const sal_uInt32 points = pPoints[poly];
-        if (points > 1)
+        sal_uInt32 nPoints = pPoints[nPolygon];
+        if (nPoints)
         {
-            pointVector.reserve(points);
-            const SalPoint* p = pPtAry[poly];
-            for (sal_uInt32 i = 0; i < points; ++i)
-                pointVector.emplace_back(SkPoint::Make(p->mnX, p->mnY));
-            path.addPoly(pointVector.data(), points, true);
+            PCONSTSALPOINT pSalPoints = pPtAry[nPolygon];
+            basegfx::B2DPolygon aPolygon;
+            aPolygon.append(basegfx::B2DPoint(pSalPoints->mnX, pSalPoints->mnY), nPoints);
+            for (sal_uInt32 i = 1; i < nPoints; ++i)
+                aPolygon.setB2DPoint(i, basegfx::B2DPoint(pSalPoints[i].mnX, pSalPoints[i].mnY));
+
+            aPolyPolygon.append(aPolygon);
         }
     }
-    SkPaint paint;
+
+    drawPolyPolygon(basegfx::B2DHomMatrix(), aPolyPolygon, 0.0);
+}
+
+bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
+                                          const basegfx::B2DPolyPolygon& rPolyPolygon,
+                                          double fTransparency)
+{
+    (void)rObjectToDevice;
+
+    const bool bHasFill(mFillColor != SALCOLOR_NONE);
+    const bool bHasLine(mLineColor != SALCOLOR_NONE);
+
+    if (rPolyPolygon.count() == 0 || !(bHasFill || bHasLine) || fTransparency < 0.0
+        || fTransparency >= 1.0)
+        return true;
+
+    preDraw();
+
+    SkPath aPath;
+    lclPolyPolygonToPath(rPolyPolygon, aPath);
+
+    SkPaint aPaint;
     if (mFillColor != SALCOLOR_NONE)
     {
-        paint.setColor(toSkColor(mFillColor));
-        paint.setStyle(SkPaint::kFill_Style);
-        mSurface->getCanvas()->drawPath(path, paint);
+        aPaint.setColor(toSkColorWithTransparency(mFillColor, fTransparency));
+        aPaint.setStyle(SkPaint::kFill_Style);
+        mSurface->getCanvas()->drawPath(aPath, aPaint);
     }
     if (mLineColor != SALCOLOR_NONE)
     {
-        paint.setColor(toSkColor(mLineColor));
-        paint.setStyle(SkPaint::kStroke_Style);
-        mSurface->getCanvas()->drawPath(path, paint);
+        aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
+        aPaint.setStyle(SkPaint::kStroke_Style);
+        mSurface->getCanvas()->drawPath(aPath, aPaint);
     }
     postDraw();
+    return true;
 }
 
-bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectToDevice,
-                                          const basegfx::B2DPolyPolygon&, double fTransparency)
-{
-    if (mLineColor == SALCOLOR_NONE && mFillColor == SALCOLOR_NONE)
-        return true;
-    (void)rObjectToDevice;
-    (void)fTransparency;
-    return false;
-}
-
+// TODO implement rObjectToDevice - need to take the matrix into account
 bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
-                                       const basegfx::B2DPolygon&, double fTransparency,
-                                       const basegfx::B2DVector& rLineWidths, basegfx::B2DLineJoin,
-                                       css::drawing::LineCap, double fMiterMinimumAngle,
+                                       const basegfx::B2DPolygon& rPolyLine, double fTransparency,
+                                       const basegfx::B2DVector& rLineWidths,
+                                       basegfx::B2DLineJoin eLineJoin,
+                                       css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
                                        bool bPixelSnapHairline)
 {
-    (void)rObjectToDevice;
-    (void)fTransparency;
-    (void)rLineWidths;
-    (void)fMiterMinimumAngle;
     (void)bPixelSnapHairline;
-    return false;
+
+    if (rPolyLine.count() == 0 || fTransparency < 0.0 || fTransparency >= 1.0
+        || mLineColor == SALCOLOR_NONE)
+        return true;
+
+    preDraw();
+
+    basegfx::B2DVector aLineWidths(rLineWidths);
+    const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
+    const basegfx::B2DVector aDeviceLineWidths(
+        bObjectToDeviceIsIdentity ? rLineWidths : rObjectToDevice * rLineWidths);
+    const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && aDeviceLineWidths.getX() < 1.0
+                                 && aLineWidths.getX() >= 1.0);
+
+    // on-demand inverse of ObjectToDevice transformation
+    basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+    if (bCorrectLineWidth)
+    {
+        if (aObjectToDeviceInv.isIdentity())
+        {
+            aObjectToDeviceInv = rObjectToDevice;
+            aObjectToDeviceInv.invert();
+        }
+
+        // calculate-back logical LineWidth for a hairline
+        aLineWidths = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0);
+    }
+
+    // Setup Line Join
+    SkPaint::Join eSkLineJoin = SkPaint::kMiter_Join;
+    switch (eLineJoin)
+    {
+        case basegfx::B2DLineJoin::Bevel:
+            eSkLineJoin = SkPaint::kBevel_Join;
+            break;
+        case basegfx::B2DLineJoin::Round:
+            eSkLineJoin = SkPaint::kRound_Join;
+            break;
+        case basegfx::B2DLineJoin::NONE:
+        case basegfx::B2DLineJoin::Miter:
+            eSkLineJoin = SkPaint::kMiter_Join;
+            break;
+    }
+
+    // convert miter minimum angle to miter limit
+    double fMiterLimit = 1.0 / std::sin(fMiterMinimumAngle / 2.0);
+
+    // Setup Line Cap
+    SkPaint::Cap eSkLineCap(SkPaint::kButt_Cap);
+
+    switch (eLineCap)
+    {
+        case css::drawing::LineCap_ROUND:
+            eSkLineCap = SkPaint::kRound_Cap;
+            break;
+        case css::drawing::LineCap_SQUARE:
+            eSkLineCap = SkPaint::kSquare_Cap;
+            break;
+        default: // css::drawing::LineCap_BUTT:
+            eSkLineCap = SkPaint::kButt_Cap;
+            break;
+    }
+
+    SkPaint aPaint;
+    aPaint.setStyle(SkPaint::kStroke_Style);
+    aPaint.setStrokeCap(eSkLineCap);
+    aPaint.setStrokeJoin(eSkLineJoin);
+    aPaint.setColor(toSkColorWithTransparency(mLineColor, fTransparency));
+    aPaint.setStrokeMiter(fMiterLimit);
+    aPaint.setStrokeWidth(aLineWidths.getX());
+    aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());
+
+    SkPath aPath;
+    lclPolygonToPath(rPolyLine, aPath);
+    SkMatrix matrix = SkMatrix::MakeTrans(0.5, 0.5);
+    {
+        SkAutoCanvasRestore autoRestore(mSurface->getCanvas(), true);
+        mSurface->getCanvas()->concat(matrix);
+        mSurface->getCanvas()->drawPath(aPath, aPaint);
+    }
+    postDraw();
+
+    return true;
 }
 
 bool SkiaSalGraphicsImpl::drawPolyLineBezier(sal_uInt32 nPoints, const SalPoint* pPtAry,


More information about the Libreoffice-commits mailing list