summaryrefslogtreecommitdiff
path: root/drawinglayer/source
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <Armin.Le.Grand@me.com>2024-08-15 11:29:28 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2024-08-15 16:05:41 +0200
commit6ab0c616d72f224c5de87b8afd2b228c223bb056 (patch)
tree7529792586e21cf6cb6bb63fe2aae4ac39ff43dc /drawinglayer/source
parent0e4266dd904ffb79c84f6b281bd822460cc64fde (diff)
CairoSDPR: Add outline to direct text rendering
Change-Id: I8c8820066a402653bf0f49bd78a98be692530e42 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171892 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
Diffstat (limited to 'drawinglayer/source')
-rw-r--r--drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx10
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx149
2 files changed, 112 insertions, 47 deletions
diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
index 41f7299c4486..497f8c2dbe40 100644
--- a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
@@ -211,11 +211,11 @@ namespace drawinglayer::primitive2d
aTransform.rotate(fRotate);
aTransform.translate(aTranslate.getX(), aTranslate.getY());
- // add transform primitive
- xRetval =
- new TransformPrimitive2D(
- aTransform,
- Primitive2DContainer{xRetval});
+ // add original and transform primitive to a GroupPrimitive2D
+ xRetval = new GroupPrimitive2D({
+ xRetval, new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer{xRetval}) });
}
return xRetval;
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index dfbce4af0c70..942a07290ece 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -2932,7 +2932,7 @@ void CairoPixelProcessor2D::renderTextBackground(
void CairoPixelProcessor2D::renderSalLayout(const std::unique_ptr<SalLayout>& rSalLayout,
const basegfx::BColor& rTextColor,
const basegfx::B2DHomMatrix& rTransform,
- bool bAntiAliase)
+ bool bAntiAliase) const
{
cairo_save(mpRT);
cairo_matrix_t aMatrix;
@@ -2943,34 +2943,13 @@ void CairoPixelProcessor2D::renderSalLayout(const std::unique_ptr<SalLayout>& rS
cairo_restore(mpRT);
}
-void CairoPixelProcessor2D::renderShadowTextDecoration(
- const basegfx::BColor& rShadowColor, const basegfx::B2DHomMatrix& rShadowObjectTransform,
+void CairoPixelProcessor2D::renderTextDecorationWithOptionalTransformAndColor(
const primitive2d::TextDecoratedPortionPrimitive2D& rDecoratedCandidate,
- const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans)
+ const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans,
+ const basegfx::B2DHomMatrix* pOptionalObjectTransform, const basegfx::BColor* pReplacementColor)
{
- // modify ColorStack as needed
- maBColorModifierStack.push(std::make_shared<basegfx::BColorModifier_replace>(rShadowColor));
-
- // modify transformation as needed
- const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
- geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
- aViewInformation2D.setObjectTransformation(rShadowObjectTransform);
- updateViewInformation(aViewInformation2D);
-
- // render same primitives as for non-shadow, but with mods set above
- renderTextDecoration(rDecoratedCandidate, rDecTrans);
-
- // restore mods
- updateViewInformation(aLastViewInformation2D);
- maBColorModifierStack.pop();
-}
-
-void CairoPixelProcessor2D::renderTextDecoration(
- const primitive2d::TextDecoratedPortionPrimitive2D& rDecoratedCandidate,
- const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans)
-{
- // get decorations from Primitive, guaranteed the same as
- // a decomposition would create
+ // get decorations from Primitive (using original TextTransform),
+ // guaranteed the same visualization as a decomposition would create
const primitive2d::Primitive2DContainer& rDecorationGeometryContent(
rDecoratedCandidate.getOrCreateDecorationGeometryContent(
rDecTrans, rDecoratedCandidate.getText(), rDecoratedCandidate.getTextPosition(),
@@ -2982,7 +2961,28 @@ void CairoPixelProcessor2D::renderTextDecoration(
return;
}
+ // modify ColorStack as needed - if needed
+ if (nullptr != pReplacementColor)
+ maBColorModifierStack.push(
+ std::make_shared<basegfx::BColorModifier_replace>(*pReplacementColor));
+
+ // modify transformation as needed - if needed
+ const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
+ if (nullptr != pOptionalObjectTransform)
+ {
+ geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+ aViewInformation2D.setObjectTransformation(*pOptionalObjectTransform);
+ updateViewInformation(aViewInformation2D);
+ }
+
+ // render primitives
process(rDecorationGeometryContent);
+
+ // restore mods
+ if (nullptr != pOptionalObjectTransform)
+ updateViewInformation(aLastViewInformation2D);
+ if (nullptr != pReplacementColor)
+ maBColorModifierStack.pop();
}
void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
@@ -3030,9 +3030,9 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
pSalLayout->GetTextWidth());
}
+ // prepare flags that are only needed if Text is Decorated
bool bHasTextRelief(false);
bool bHasShadow(false);
- bool bHasOutline(false);
bool bHasTextDecoration(false);
if (nullptr != pDecoratedCandidate)
@@ -3040,7 +3040,6 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
// outline AND shadow depend on NO TextRelief (see dialog)
bHasTextRelief = primitive2d::TEXT_RELIEF_NONE != pDecoratedCandidate->getTextRelief();
bHasShadow = !bHasTextRelief && pDecoratedCandidate->getShadow();
- bHasOutline = !bHasTextRelief && pDecoratedCandidate->getFontAttribute().getOutline();
// check if TextDecoration is needed
bHasTextDecoration
@@ -3051,6 +3050,10 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
!= pDecoratedCandidate->getTextEmphasisMark();
}
+ // prepare flags for non-decorated afetr that, these *might* be
+ // dependent on flags above
+ bool bHasOutline(!bHasTextRelief && rTextCandidate.getFontAttribute().getOutline());
+
if (bHasShadow)
{
// Text shadow is constant, relative to font size, *not* rotated with
@@ -3064,6 +3067,7 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
if (aBlack == rTextCandidate.getFontColor()
|| rTextCandidate.getFontColor().luminance() < (8.0 / 255.0))
aShadowColor = COL_LIGHTGRAY.getBColor();
+ aShadowColor = maBColorModifierStack.getModifiedColor(aShadowColor);
// create shadow offset
const basegfx::B2DHomMatrix aShadowTransform(
@@ -3082,30 +3086,91 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
if (bHasTextDecoration)
{
- // this renders the same as renderTextDecoration, but encapsulated
- // in temporary ColorStack & transformation modifications
const basegfx::B2DHomMatrix aTransform(getViewInformation2D().getObjectTransformation()
* aShadowTransform);
- renderShadowTextDecoration(aShadowColor, aTransform, *pDecoratedCandidate, aDecTrans);
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans,
+ &aTransform, &aShadowColor);
}
}
- if (bHasOutline)
+ if (bHasTextRelief)
{
// todo
}
+ else if (bHasOutline)
+ {
+ // render as outline
+ basegfx::BColor aTextColor(
+ maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
+ basegfx::B2DHomMatrix aInvViewTransform;
+
+ // discrete offsets defined here to easily allow to change them,
+ // e.g. if more 'fat' outline is wanted, it may be incerased to 1.5
+ constexpr double fZero(0.0);
+ constexpr double fPlus(1.0);
+ constexpr double fMinus(-1.0);
- // render text
- const basegfx::BColor aRGBFontColor(
- maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
- renderSalLayout(pSalLayout, aRGBFontColor, aFullTextTransform,
- getViewInformation2D().getUseAntiAliasing());
+ constexpr std::array<std::pair<double, double>, 8> offsets{
+ std::pair<double, double>{ fMinus, fMinus }, std::pair<double, double>{ fZero, fMinus },
+ std::pair<double, double>{ fPlus, fMinus }, std::pair<double, double>{ fMinus, fZero },
+ std::pair<double, double>{ fPlus, fZero }, std::pair<double, double>{ fMinus, fPlus },
+ std::pair<double, double>{ fZero, fPlus }, std::pair<double, double>{ fPlus, fPlus }
+ };
+
+ if (bHasTextDecoration)
+ {
+ // to use discrete offset (pixels) we will need the back-transform from
+ // discrete view coordinates to 'world' coordinates (logic view coordinates),
+ // this is the inverse ViewTransformation.
+ // NOTE: Alternatively we could calculate the lengths for fPlus/fMinus in
+ // logic view coordinates, but would need to create another B2DHomMatrix and
+ // to do it correct would need to handle two vectors holding the directions,
+ // else - if ever someone will rotate/shear that transformation - it would
+ // break
+ aInvViewTransform = getViewInformation2D().getViewTransformation();
+ aInvViewTransform.invert();
+ }
+
+ for (const auto& offset : offsets)
+ {
+ const basegfx::B2DHomMatrix aDiscreteOffset(
+ basegfx::utils::createTranslateB2DHomMatrix(offset.first, offset.second));
+ renderSalLayout(pSalLayout, aTextColor, aDiscreteOffset * aFullTextTransform,
+ getViewInformation2D().getUseAntiAliasing());
+ if (bHasTextDecoration)
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ aInvViewTransform * aDiscreteOffset
+ * getViewInformation2D().getObjectToViewTransformation());
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans,
+ &aTransform);
+ }
+ }
- if (bHasTextDecoration)
+ // at (center, center) paint in COL_WHITE
+ aTextColor = maBColorModifierStack.getModifiedColor(COL_WHITE.getBColor());
+ renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+ getViewInformation2D().getUseAntiAliasing());
+ if (bHasTextDecoration)
+ {
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans,
+ nullptr, &aTextColor);
+ }
+ }
+ else
{
- // render using same geometry/primitives that a decompose would
- // create -> safe to get the same visualization for both
- renderTextDecoration(*pDecoratedCandidate, aDecTrans);
+ // render text
+ const basegfx::BColor aTextColor(
+ maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
+ renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+ getViewInformation2D().getUseAntiAliasing());
+
+ if (bHasTextDecoration)
+ {
+ // render using same geometry/primitives that a decompose would
+ // create -> safe to get the same visualization for both
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans);
+ }
}
}