diff options
author | Armin Le Grand (Collabora) <Armin.Le.Grand@me.com> | 2024-08-02 15:56:52 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2024-08-05 12:29:02 +0200 |
commit | bf9e8d5ffd3f29fa027f0009a70f27d3456b648b (patch) | |
tree | 90a472d181b256d0571c603c7494a326cf89a2f5 /drawinglayer/source | |
parent | e6d470293243f1a8dd3dbcb42258dd22e408e127 (diff) |
CairoSDPR: direct text rendering using Cairo
I have added basic support for the text primitives
(TextSimplePortionPrimitive2D) to the SDPR Cairo
renderer. It can now basically render Text using
a fallback to the already existing CairoTextRender.
NOTE: This is not yet complete, but for discussion.
Change-Id: I9b0c7b6bb4892905576593ef4e2b4071c7663c63
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171429
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'drawinglayer/source')
-rw-r--r-- | drawinglayer/source/primitive2d/textlayoutdevice.cxx | 32 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 158 |
2 files changed, 189 insertions, 1 deletions
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx index 3daecb4bec62..3a6667d0461f 100644 --- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx +++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx @@ -38,6 +38,8 @@ #include <vcl/metric.hxx> #include <i18nlangtag/languagetag.hxx> #include <vcl/svapp.hxx> +#include <vcl/vcllayout.hxx> +#include <vcl/glyphitemcache.hxx> namespace drawinglayer::primitive2d { @@ -192,6 +194,21 @@ void TextLayouterDevice::setFontAttribute(const attribute::FontAttribute& rFontA } } +void TextLayouterDevice::setLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode) +{ + mrDevice.SetLayoutMode(nTextLayoutMode); +} + +vcl::text::ComplexTextLayoutFlags TextLayouterDevice::getLayoutMode() const +{ + return mrDevice.GetLayoutMode(); +} + +void TextLayouterDevice::setTextColor(const basegfx::BColor& rColor) +{ + mrDevice.SetTextColor(Color(rColor)); +} + double TextLayouterDevice::getOverlineOffset() const { const ::FontMetric& rMetric = mrDevice.GetFontMetric(); @@ -348,6 +365,21 @@ std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_ return aRetval; } +std::unique_ptr<SalLayout> TextLayouterDevice::getSalLayout(const OUString& rText, + sal_uInt32 nIndex, sal_uInt32 nLength, + const basegfx::B2DPoint& rStartPoint, + const KernArray& rDXArray, + std::span<const sal_Bool> pKashidaAry) +{ + const SalLayoutGlyphs* pGlyphs( + SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&mrDevice, rText, nIndex, nLength)); + const Point aStartPoint(basegfx::fround<tools::Long>(rStartPoint.getX()), + basegfx::fround<tools::Long>(rStartPoint.getY())); + KernArraySpan aKernArraySpan(rDXArray); + return mrDevice.ImplLayout(rText, nIndex, nLength, aStartPoint, 0, aKernArraySpan, pKashidaAry, + SalLayoutFlags::NONE, nullptr, pGlyphs); +} + // helper methods for vcl font handling vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute, diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index f6483e4a795e..baea016068c1 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -39,12 +39,17 @@ #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> #include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx> #include <drawinglayer/primitive2d/BitmapAlphaPrimitive2D.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> #include <drawinglayer/converters.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <basegfx/curve/b2dcubicbezier.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <basegfx/utils/systemdependentdata.hxx> #include <basegfx/utils/bgradient.hxx> #include <vcl/BitmapReadAccess.hxx> +#include <officecfg/Office/Common.hxx> +#include <vcl/vcllayout.hxx> #include <unordered_map> #include <dlfcn.h> @@ -285,7 +290,7 @@ void checkAndDoPixelSnap(cairo_t* pRT, // with the comments above at CairoPathHelper we cannot do PixelSnap // at path construction time, so it needs to be done *after* the path - // data is added to the cairo context. ADvantage is that all general + // data is added to the cairo context. Advantage is that all general // path data can be buffered, though, but needs view-dependent manipulation // here after being added. // For now, just snap all points - no real need to identify hor/ver lines @@ -807,6 +812,10 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& : BaseProcessor2D(rViewInformation) , maBColorModifierStack() , mpRT(nullptr) + , mbRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , mbRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) { if (pTarget) { @@ -2793,6 +2802,141 @@ void CairoPixelProcessor2D::processBitmapAlphaPrimitive2D( rBitmapAlphaPrimitive2D.getTransparency()); } +void CairoPixelProcessor2D::processTextSimplePortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rCandidate) +{ + if (SAL_LIKELY(mbRenderSimpleTextDirect)) + { + renderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); + } + else + { + process(rCandidate); + } +} + +void CairoPixelProcessor2D::processTextDecoratedPortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rCandidate) +{ + if (SAL_LIKELY(mbRenderDecoratedTextDirect)) + { + renderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); + } + else + { + process(rCandidate); + } +} + +void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) +{ + // decompose matrix to have global position and scale of text + const basegfx::B2DHomMatrix aGlobalFontTransform( + getViewInformation2D().getObjectToViewTransformation() * rTextCandidate.getTextTransform()); + basegfx::B2DVector aGlobalFontScaling, aGlobalFontTranslate; + double fGlobalFontRotate, fGlobalFontShearX; + aGlobalFontTransform.decompose(aGlobalFontScaling, aGlobalFontTranslate, fGlobalFontRotate, + fGlobalFontShearX); + + // decompose primitive-local matrix to get local font scaling + double fLocalFontRotate, fLocalFontShearX; + basegfx::B2DVector aLocalFontSize, aLocalFontTranslate; + rTextCandidate.getTextTransform().decompose(aLocalFontSize, aLocalFontTranslate, + fLocalFontRotate, fLocalFontShearX); + + // Get the VCL font from existing processor tooling. Do not use + // rotation, for Cairo we can transform the whole text render and + // thus handle the xext in it's local coordinate system untransformed + vcl::Font aFont(primitive2d::getVclFontFromFontAttribute( + rTextCandidate.getFontAttribute(), aGlobalFontScaling.getX(), aGlobalFontScaling.getY(), + 0.0, rTextCandidate.getLocale())); + + if (aFont.GetFontSize().Height() <= 0) + { + // Don't draw fonts without height, error. use decompose as fallback + process(rTextCandidate); + return; + } + + // set FillColor Attribute at Font + Color aFillColor( + maBColorModifierStack.getModifiedColor(rTextCandidate.getTextFillColor().getBColor())); + aFont.SetTransparent(rTextCandidate.getTextFillColor().IsTransparent()); + if (rTextCandidate.getTextFillColor().IsTransparent()) + aFillColor.SetAlpha(rTextCandidate.getTextFillColor().GetAlpha()); + aFont.SetFillColor(aFillColor); + + // create integer DXArray. As mentioned above we can act in the + // Text's local coordinate system without transformation at all + const ::std::vector<double>& rDXArray(rTextCandidate.getDXArray()); + KernArray aDXArray; + + if (!rDXArray.empty()) + { + aDXArray.reserve(rDXArray.size()); + for (auto const& elem : rDXArray) + aDXArray.push_back(basegfx::fround(elem)); + } + + // set parameters and paint text snippet + const basegfx::BColor aRGBFontColor( + maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); + + // create a TextLayouter to access encapsulated VCL Text/Font related tooling + primitive2d::TextLayouterDevice aTextLayouter; + aTextLayouter.setFontAttribute(rTextCandidate.getFontAttribute(), aLocalFontSize.getX(), + aLocalFontSize.getY(), rTextCandidate.getLocale()); + aTextLayouter.setTextColor(aRGBFontColor); + + if (rTextCandidate.getFontAttribute().getRTL()) + { + vcl::text::ComplexTextLayoutFlags nRTLLayoutMode( + aTextLayouter.getLayoutMode() & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong); + nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl + | vcl::text::ComplexTextLayoutFlags::TextOriginLeft; + aTextLayouter.setLayoutMode(nRTLLayoutMode); + } + else + { + // tdf#101686: This is LTR text, but the output device may have RTL state. + vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(aTextLayouter.getLayoutMode()); + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl; + nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong; + aTextLayouter.setLayoutMode(nLTRLayoutMode); + } + + // create SalLayout. No need for a position, as mentioned text can work + // without transformations, so start point is always 0,0 + std::unique_ptr<SalLayout> pSalLayout(aTextLayouter.getSalLayout( + rTextCandidate.getText(), rTextCandidate.getTextPosition(), rTextCandidate.getTextLength(), + basegfx::B2DPoint(0.0, 0.0), aDXArray, rTextCandidate.getKashidaArray())); + + if (!pSalLayout) + { + // got no layout, error. use decompose as fallback + process(rTextCandidate); + return; + } + + // draw using Cairo, use existing tooling (this tunnels to + // CairoTextRender::ImplDrawTextLayout) + cairo_save(mpRT); + const basegfx::B2DHomMatrix aObjTransformWithoutScale( + basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fLocalFontShearX, fLocalFontRotate, + aLocalFontTranslate)); + const basegfx::B2DHomMatrix aFullTextTransform( + getViewInformation2D().getObjectToViewTransformation() * aObjTransformWithoutScale); + + cairo_matrix_t aMatrix; + cairo_matrix_init(&aMatrix, aFullTextTransform.a(), aFullTextTransform.b(), + aFullTextTransform.c(), aFullTextTransform.d(), aFullTextTransform.e(), + aFullTextTransform.f()); + cairo_set_matrix(mpRT, &aMatrix); + pSalLayout->drawSalLayout(mpRT, aRGBFontColor, getViewInformation2D().getUseAntiAliasing()); + cairo_restore(mpRT); +} + void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { switch (rCandidate.getPrimitive2DID()) @@ -2921,6 +3065,18 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit static_cast<const primitive2d::BitmapAlphaPrimitive2D&>(rCandidate)); break; } + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: + { + processTextSimplePortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: + { + processTextDecoratedPortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; + } // continue with decompose default: |