summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2018-08-30 23:41:36 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2018-08-31 19:28:29 +0200
commitb5f081e1ac14f60497f62a27be86b07b0baa42f7 (patch)
tree86dfda11c47f96d9c640b76dd01c13a7897b2c0c
parentcecf71c18da5430c10daa8522d38d5144edefc14 (diff)
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@collabora.co.uk> Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
-rw-r--r--basegfx/source/matrix/b2dhommatrix.cxx14
-rw-r--r--vcl/inc/salgdi.hxx7
-rw-r--r--vcl/source/gdi/salgdilayout.cxx242
3 files changed, 170 insertions, 93 deletions
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 )