[Libreoffice-commits] core.git: basegfx/source vcl/inc vcl/source

Libreoffice Gerrit user logerrit at kemper.freedesktop.org
Fri Aug 31 17:28:56 UTC 2018


 basegfx/source/matrix/b2dhommatrix.cxx |   14 +
 vcl/inc/salgdi.hxx                     |    7 
 vcl/source/gdi/salgdilayout.cxx        |  242 ++++++++++++++++++++-------------
 3 files changed, 170 insertions(+), 93 deletions(-)

New commits:
commit b5f081e1ac14f60497f62a27be86b07b0baa42f7
Author:     Armin Le Grand <Armin.Le.Grand at cib.de>
AuthorDate: Thu Aug 30 23:41:36 2018 +0200
Commit:     Armin Le Grand <Armin.Le.Grand at cib.de>
CommitDate: Fri Aug 31 19:28:29 2018 +0200

    Support RTL layout in VCL using Matrices
    
    Did some changes inspired by Noel, corrected the
    transformation due to mirroring already applied,
    re-added 'mirror this window back'-mode
    
    Change-Id: I21999e59898cf672fd293bc2b11f5d568e6673be
    Reviewed-on: https://gerrit.libreoffice.org/59842
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at cib.de>

diff --git a/basegfx/source/matrix/b2dhommatrix.cxx b/basegfx/source/matrix/b2dhommatrix.cxx
index 1e991304a4f0..61178c496011 100644
--- a/basegfx/source/matrix/b2dhommatrix.cxx
+++ b/basegfx/source/matrix/b2dhommatrix.cxx
@@ -148,8 +148,20 @@ namespace basegfx
 
     B2DHomMatrix& B2DHomMatrix::operator*=(const B2DHomMatrix& rMat)
     {
-        if(!rMat.isIdentity())
+        if(rMat.isIdentity())
+        {
+            // multiply with identity, no change -> nothing to do
+        }
+        else if(isIdentity())
+        {
+            // we are identity, result will be rMat -> assign
+            *this = rMat;
+        }
+        else
+        {
+            // multiply
             mpImpl->doMulMatrix(*rMat.mpImpl);
+        }
 
         return *this;
     }
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 866a10b5beb4..94ceb141bb44 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -25,6 +25,7 @@
 #include "impfontmetricdata.hxx"
 #include "salgdiimpl.hxx"
 #include "sallayout.hxx"
+#include <basegfx/matrix/b2dhommatrix.hxx>
 
 #include <config_cairo_canvas.h>
 
@@ -218,6 +219,8 @@ public:
     basegfx::B2DPoint           mirror( const basegfx::B2DPoint& i_rPoint, const OutputDevice *pOutDev ) const;
     basegfx::B2DPolygon         mirror( const basegfx::B2DPolygon& i_rPoly, const OutputDevice *pOutDev ) const;
     basegfx::B2DPolyPolygon     mirror( const basegfx::B2DPolyPolygon& i_rPoly, const OutputDevice *pOutDev ) const;
+    const basegfx::B2DHomMatrix& getMirror( const OutputDevice *pOutDev ) const;
+    basegfx::B2DHomMatrix       mirror( const basegfx::B2DHomMatrix& i_rMatrix, const OutputDevice *pOutDev ) const;
 
     // non virtual methods; these do possible coordinate mirroring and
     // then delegate to protected virtual methods
@@ -655,6 +658,10 @@ protected:
 private:
     SalLayoutFlags              m_nLayout; //< 0: mirroring off, 1: mirror x-axis
 
+    // for buffering the Mirror-Matrix, see ::getMirror
+    basegfx::B2DHomMatrix       m_aLastMirror;
+    long                        m_aLastMirrorW;
+
 protected:
     /// flags which hold the SetAntialiasing() value from OutputDevice
     bool                        m_bAntiAliasB2DDraw : 1;
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index 16396a60a98b..64fba8edf01d 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -34,6 +34,7 @@
 #include <basegfx/utils/systemdependentdata.hxx>
 #include <cppuhelper/basemutex.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
 #include <o3tl/make_unique.hxx>
 
 // The only common SalFrame method
@@ -55,6 +56,8 @@ SalFrameGeometry SalFrame::GetGeometry()
 
 SalGraphics::SalGraphics()
 :   m_nLayout( SalLayoutFlags::NONE ),
+    m_aLastMirror(),
+    m_aLastMirrorW(0),
     m_bAntiAliasB2DDraw(false)
 {
     // read global RTL settings
@@ -406,84 +409,112 @@ void SalGraphics::mirror( tools::Rectangle& rRect, const OutputDevice *pOutDev,
     rRect.Move( x - x_org, 0 );
 }
 
-basegfx::B2DPoint SalGraphics::mirror( const basegfx::B2DPoint& i_rPoint, const OutputDevice *i_pOutDev ) const
+basegfx::B2DPoint SalGraphics::mirror( const basegfx::B2DPoint& i_rPoint, const OutputDevice* i_pOutDev ) const
 {
-    long w;
-    if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV )
-        w = i_pOutDev->GetOutputWidthPixel();
+    const basegfx::B2DHomMatrix& rMirror(getMirror(i_pOutDev));
+
+    if(rMirror.isIdentity())
+    {
+        return i_rPoint;
+    }
     else
-        w = GetGraphicsWidth();
+    {
+        return rMirror * i_rPoint;
+    }
+}
 
-    SAL_WARN_IF( !w, "vcl", "missing graphics width" );
+basegfx::B2DPolygon SalGraphics::mirror( const basegfx::B2DPolygon& i_rPoly, const OutputDevice* i_pOutDev ) const
+{
+    const basegfx::B2DHomMatrix& rMirror(getMirror(i_pOutDev));
 
-    basegfx::B2DPoint aRet( i_rPoint );
-    if( w )
+    if(rMirror.isIdentity())
     {
-        if( i_pOutDev && !i_pOutDev->IsRTLEnabled() )
-        {
-            OutputDevice *pOutDevRef = const_cast<OutputDevice*>(i_pOutDev);
-            // mirror this window back
-            double devX = w-pOutDevRef->GetOutputWidthPixel()-pOutDevRef->GetOutOffXPixel();   // re-mirrored mnOutOffX
-            aRet.setX( devX + (i_rPoint.getX() - pOutDevRef->GetOutOffXPixel()) );
-        }
-        else
-            aRet.setX( w-1-i_rPoint.getX() );
+        return i_rPoly;
+    }
+    else
+    {
+        basegfx::B2DPolygon aRet(i_rPoly);
+        aRet.transform(rMirror);
+        aRet.flip();
+        return aRet;
     }
-    return aRet;
 }
 
-basegfx::B2DPolygon SalGraphics::mirror( const basegfx::B2DPolygon& i_rPoly, const OutputDevice *i_pOutDev ) const
+basegfx::B2DPolyPolygon SalGraphics::mirror( const basegfx::B2DPolyPolygon& i_rPoly, const OutputDevice* i_pOutDev ) const
 {
-    long w;
-    if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV )
-        w = i_pOutDev->GetOutputWidthPixel();
+    const basegfx::B2DHomMatrix& rMirror(getMirror(i_pOutDev));
+
+    if(rMirror.isIdentity())
+    {
+        return i_rPoly;
+    }
     else
-        w = GetGraphicsWidth();
+    {
+        basegfx::B2DPolyPolygon aRet(i_rPoly);
+        aRet.transform(rMirror);
+        aRet.flip();
+        return aRet;
+    }
+}
 
+const basegfx::B2DHomMatrix& SalGraphics::getMirror( const OutputDevice* i_pOutDev ) const
+{
+    // get mirroring transformation
+    const long w(nullptr != i_pOutDev && OUTDEV_VIRDEV == i_pOutDev->GetOutDevType()
+        ? i_pOutDev->GetOutputWidthPixel()
+        : GetGraphicsWidth());
     SAL_WARN_IF( !w, "vcl", "missing graphics width" );
 
-    basegfx::B2DPolygon aRet;
-    if( w )
+    if(w != m_aLastMirrorW)
     {
-        sal_Int32 nPoints = i_rPoly.count();
-        for( sal_Int32 i = 0; i < nPoints; i++ )
+        const_cast<SalGraphics*>(this)->m_aLastMirrorW = w;
+
+        if(w)
         {
-            aRet.append( mirror( i_rPoly.getB2DPoint( i ), i_pOutDev ) );
-            if( i_rPoly.isPrevControlPointUsed( i ) )
-                aRet.setPrevControlPoint( i, mirror( i_rPoly.getPrevControlPoint( i ), i_pOutDev ) );
-            if( i_rPoly.isNextControlPointUsed( i ) )
-                aRet.setNextControlPoint( i, mirror( i_rPoly.getNextControlPoint( i ), i_pOutDev ) );
+            if(nullptr != i_pOutDev && !i_pOutDev->IsRTLEnabled())
+            {
+                // Original code was (removed here already pOutDevRef->i_pOutDev):
+                //      // mirror this window back
+                //      double devX = w-i_pOutDev->GetOutputWidthPixel()-i_pOutDev->GetOutOffXPixel();   // re-mirrored mnOutOffX
+                //      aRet.setX( devX + (i_rPoint.getX() - i_pOutDev->GetOutOffXPixel()) );
+                // I do not reaaly understand the comment 'mirror this window back', so cannot guarantee
+                // that this works as before, but I have reduced this (by re-placing and re-formatting) to
+                // a simple translation:
+                const_cast<SalGraphics*>(this)->m_aLastMirror = basegfx::utils::createTranslateB2DHomMatrix(
+                    w - i_pOutDev->GetOutputWidthPixel() - (2 * i_pOutDev->GetOutOffXPixel()),
+                    0.0);
+            }
+            else
+            {
+                // Original code was:
+                //      aRet.setX( w-1-i_rPoint.getX() );
+                // -mirror X -> scale(-1.0, 1.0)
+                // -translate X -> translate(w-1, 0); but already mirrored, so use translate(1-w, 0)
+                // Checked this one, works as expected.
+                const_cast<SalGraphics*>(this)->m_aLastMirror = basegfx::utils::createScaleTranslateB2DHomMatrix(
+                    -1.0,
+                    1.0,
+                    1-w,
+                    0.0);
+            }
+        }
+        else
+        {
+            const_cast<SalGraphics*>(this)->m_aLastMirror.identity();
         }
-        aRet.setClosed( i_rPoly.isClosed() );
-        aRet.flip();
     }
-    else
-        aRet = i_rPoly;
-    return aRet;
+
+    return m_aLastMirror;
 }
 
-basegfx::B2DPolyPolygon SalGraphics::mirror( const basegfx::B2DPolyPolygon& i_rPoly, const OutputDevice *i_pOutDev ) const
+basegfx::B2DHomMatrix SalGraphics::mirror( const basegfx::B2DHomMatrix& i_rMatrix, const OutputDevice *pOutDev ) const
 {
-    long w;
-    if( i_pOutDev && i_pOutDev->GetOutDevType() == OUTDEV_VIRDEV )
-        w = i_pOutDev->GetOutputWidthPixel();
-    else
-        w = GetGraphicsWidth();
-
-    SAL_WARN_IF( !w, "vcl", "missing graphics width" );
+    // add mirroring transformation to i_rMatrix
+    const basegfx::B2DHomMatrix& rMirror(getMirror(pOutDev));
 
-    basegfx::B2DPolyPolygon aRet;
-    if( w )
-    {
-        sal_Int32 nPoly = i_rPoly.count();
-        for( sal_Int32 i = 0; i < nPoly; i++ )
-            aRet.append( mirror( i_rPoly.getB2DPolygon( i ), i_pOutDev ) );
-        aRet.setClosed( i_rPoly.isClosed() );
-        aRet.flip();
-    }
-    else
-        aRet = i_rPoly;
-    return aRet;
+    // Apply mirror to given matrix by multiply from left ('after' i_rMatrix).
+    // Identity chechs and fast-paths are in the operator
+    return rMirror * i_rMatrix;
 }
 
 bool SalGraphics::SetClipRegion( const vcl::Region& i_rClip, const OutputDevice *pOutDev )
@@ -653,46 +684,73 @@ bool SalGraphics::DrawPolyLine(
     bool bPixelSnapHairline,
     const OutputDevice* i_pOutDev)
 {
-    bool bRet = false;
-
     if( (m_nLayout & SalLayoutFlags::BiDiRtl) || (i_pOutDev && i_pOutDev->IsRTLEnabled()) )
     {
-        // if mirrored, we need to apply transformation since it is
-        // not clear what 'mirror' does - might be changed when this
-        // happens often
-        basegfx::B2DPolygon aMirror(i_rPolygon);
-
-        aMirror.transform(rObjectToDevice);
-        aMirror = mirror(aMirror, i_pOutDev);
-        // basegfx::B2DPolygon aMirror( mirror( i_rPolygon, i_pOutDev ) );
+        // mirroring set
+        const basegfx::B2DHomMatrix& rMirror(getMirror(i_pOutDev));
 
-        // also need to transform LineWidth
-        const basegfx::B2DVector aLineWidth(rObjectToDevice * i_rLineWidth);
-
-        bRet = drawPolyLine(
-            basegfx::B2DHomMatrix(), // now empty transformation, already used
-            aMirror,
-            i_fTransparency,
-            aLineWidth,
-            i_eLineJoin,
-            i_eLineCap,
-            i_fMiterMinimumAngle,
-            bPixelSnapHairline);
-    }
-    else
-    {
-        bRet = drawPolyLine(
-            rObjectToDevice,
-            i_rPolygon,
-            i_fTransparency,
-            i_rLineWidth,
-            i_eLineJoin,
-            i_eLineCap,
-            i_fMiterMinimumAngle,
-            bPixelSnapHairline);
+        if(!rMirror.isIdentity())
+        {
+            // If we really have a mirroring to apply, we *could*
+            // use the ::mirror call to modify the B2DPolygon (and
+            // prepare the LineWidth scaling), but we also
+            // can add that mirroring to rObjectToDevice transformation
+            // by using linear combination of transformations and stay
+            // on having the transformation
+            if(rObjectToDevice.isIdentity())
+            {
+                // There is no ObjectToDevice transformation set. We can just
+                // use rMirror, that would be the result of the linear combination
+                return drawPolyLine(
+                    rMirror,
+                    i_rPolygon,
+                    i_fTransparency,
+                    i_rLineWidth,
+                    i_eLineJoin,
+                    i_eLineCap,
+                    i_fMiterMinimumAngle,
+                    bPixelSnapHairline);
+            }
+            else
+            {
+                // To create the linear combination, we need to
+                // - multiply with rObjectToDevice to get to device-coordinates
+                //   (what is a simple copy)
+                // - apply rMirror (multiply from left)
+                // - multiply with inverse of rObjectToDevice to get back from
+                //   device-coordinates to object-coordinates
+                // this only makes sense to do when we *have* a ObjectToDevice
+                // transformation, so optimize that
+                basegfx::B2DHomMatrix aLinearCombination(rObjectToDevice);
+                basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
+
+                aLinearCombination = rMirror * aLinearCombination;
+                aObjectToDeviceInv.invert();
+                aLinearCombination = aObjectToDeviceInv * aLinearCombination;
+
+                return drawPolyLine(
+                    aLinearCombination,
+                    i_rPolygon,
+                    i_fTransparency,
+                    i_rLineWidth,
+                    i_eLineJoin,
+                    i_eLineCap,
+                    i_fMiterMinimumAngle,
+                    bPixelSnapHairline);
+            }
+        }
     }
 
-    return bRet;
+    // no mirroring set (or identity), use standard call
+    return drawPolyLine(
+        rObjectToDevice,
+        i_rPolygon,
+        i_fTransparency,
+        i_rLineWidth,
+        i_eLineJoin,
+        i_eLineCap,
+        i_fMiterMinimumAngle,
+        bPixelSnapHairline);
 }
 
 bool SalGraphics::DrawGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient )


More information about the Libreoffice-commits mailing list