summaryrefslogtreecommitdiff
path: root/drawinglayer/source
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <Armin.Le.Grand@me.com>2024-08-02 15:56:52 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2024-08-05 12:29:02 +0200
commitbf9e8d5ffd3f29fa027f0009a70f27d3456b648b (patch)
tree90a472d181b256d0571c603c7494a326cf89a2f5 /drawinglayer/source
parente6d470293243f1a8dd3dbcb42258dd22e408e127 (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.cxx32
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx158
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: