diff options
author | Armin Le Grand <Armin.Le.Grand@cib.de> | 2018-08-30 23:41:36 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@cib.de> | 2018-08-31 19:28:29 +0200 |
commit | b5f081e1ac14f60497f62a27be86b07b0baa42f7 (patch) | |
tree | 86dfda11c47f96d9c640b76dd01c13a7897b2c0c /vcl | |
parent | cecf71c18da5430c10daa8522d38d5144edefc14 (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>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/salgdi.hxx | 7 | ||||
-rw-r--r-- | vcl/source/gdi/salgdilayout.cxx | 242 |
2 files changed, 157 insertions, 92 deletions
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 ) |