summaryrefslogtreecommitdiff
path: root/drawinglayer
diff options
context:
space:
mode:
authorArmin Le Grand (Collabora) <Armin.Le.Grand@me.com>2024-08-15 17:30:23 +0200
committerArmin Le Grand <Armin.Le.Grand@me.com>2024-08-16 14:44:11 +0200
commitcd6529a0501d49e0cd6344523d509fa7186687c9 (patch)
tree5aeb72b40935d11de2bb1bce97e0fe2640cc9266 /drawinglayer
parent4f5d694e5840b0b9bda15dc120e8b07d18d4cbd0 (diff)
CairoSDPR: Add support for Relief for Text Rendering
Change-Id: Idcb33e0f799b2219a4e737d0421bc21d85b5c7aa Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171915 Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com> Tested-by: Jenkins
Diffstat (limited to 'drawinglayer')
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx70
-rw-r--r--drawinglayer/source/primitive2d/textprimitive2d.cxx24
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx102
3 files changed, 132 insertions, 64 deletions
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
index 2f0792fb7aa0..bfa5ebbb7eea 100644
--- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -120,6 +120,11 @@ namespace drawinglayer::primitive2d
if(bOverlineUsed)
{
+ // for Relief we have to manipulate the OverlineColor
+ basegfx::BColor aOverlineColor(getOverlineColor());
+ if (hasTextRelief() && COL_BLACK.getBColor() == aOverlineColor)
+ aOverlineColor = COL_WHITE.getBColor();
+
// create primitive geometry for overline
maBufferedDecorationGeometry.push_back(
new TextLinePrimitive2D(
@@ -128,11 +133,16 @@ namespace drawinglayer::primitive2d
aTextLayouter.getOverlineOffset(),
aTextLayouter.getOverlineHeight(),
getFontOverline(),
- getOverlineColor()));
+ aOverlineColor));
}
if(bUnderlineUsed)
{
+ // for Relief we have to manipulate the TextlineColor
+ basegfx::BColor aTextlineColor(getTextlineColor());
+ if (hasTextRelief() && COL_BLACK.getBColor() == aTextlineColor)
+ aTextlineColor = COL_WHITE.getBColor();
+
// create primitive geometry for underline
maBufferedDecorationGeometry.push_back(
new TextLinePrimitive2D(
@@ -141,11 +151,16 @@ namespace drawinglayer::primitive2d
aTextLayouter.getUnderlineOffset(),
aTextLayouter.getUnderlineHeight(),
getFontUnderline(),
- getTextlineColor()));
+ aTextlineColor));
}
if(bStrikeoutUsed)
{
+ // for Relief we have to manipulate the FontColor
+ basegfx::BColor aFontColor(getFontColor());
+ if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor)
+ aFontColor = COL_WHITE.getBColor();
+
// create primitive geometry for strikeout
if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
{
@@ -156,7 +171,7 @@ namespace drawinglayer::primitive2d
new TextCharacterStrikeoutPrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
- getFontColor(),
+ aFontColor,
aStrikeoutChar,
getFontAttribute(),
getLocale()));
@@ -168,7 +183,7 @@ namespace drawinglayer::primitive2d
new TextGeometryStrikeoutPrimitive2D(
rDecTrans.getB2DHomMatrix(),
fTextWidth,
- getFontColor(),
+ aFontColor,
aTextLayouter.getUnderlineHeight(),
aTextLayouter.getStrikeoutOffset(),
getTextStrikeout()));
@@ -182,6 +197,11 @@ namespace drawinglayer::primitive2d
if (pSalLayout)
{
+ // for Relief we have to manipulate the FontColor
+ basegfx::BColor aFontColor(getFontColor());
+ if (hasTextRelief() && COL_BLACK.getBColor() == aFontColor)
+ aFontColor = COL_WHITE.getBColor();
+
// placeholders for repeated content, only created once
Primitive2DReference aShape;
Primitive2DReference aRect1;
@@ -198,7 +218,7 @@ namespace drawinglayer::primitive2d
// the callback from OutputDevice::createEmphasisMarks providing the data
// for each EmphasisMark
- auto aEmphasisCallback([this, &aShape, &aRect1, &aRect2, &aEmphasisContent, &aObjTransformWithoutScale](
+ auto aEmphasisCallback([&aShape, &aRect1, &aRect2, &aEmphasisContent, &aObjTransformWithoutScale, &aFontColor](
const basegfx::B2DPoint& rOutPoint, const basegfx::B2DPolyPolygon& rShape,
bool isPolyLine, const tools::Rectangle& rRect1, const tools::Rectangle& rRect2)
{
@@ -212,9 +232,9 @@ namespace drawinglayer::primitive2d
if (!aShape)
{
if (isPolyLine)
- aShape = new PolyPolygonHairlinePrimitive2D(rShape, getFontColor());
+ aShape = new PolyPolygonHairlinePrimitive2D(rShape, aFontColor);
else
- aShape = new PolyPolygonColorPrimitive2D(rShape, getFontColor());
+ aShape = new PolyPolygonColorPrimitive2D(rShape, aFontColor);
}
aEmphasisContent.push_back(
@@ -228,7 +248,7 @@ namespace drawinglayer::primitive2d
// create Rectangle1 if provided
if (!aRect1)
aRect1 = new FilledRectanglePrimitive2D(
- basegfx::B2DRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom()), getFontColor());
+ basegfx::B2DRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom()), aFontColor);
aEmphasisContent.push_back(
new TransformPrimitive2D(
@@ -241,7 +261,7 @@ namespace drawinglayer::primitive2d
// create Rectangle2 if provided
if (!aRect2)
aRect2 = new FilledRectanglePrimitive2D(
- basegfx::B2DRange(rRect2.Left(), rRect2.Top(), rRect2.Right(), rRect2.Bottom()), getFontColor());
+ basegfx::B2DRange(rRect2.Left(), rRect2.Top(), rRect2.Right(), rRect2.Bottom()), aFontColor);
aEmphasisContent.push_back(
new TransformPrimitive2D(
@@ -328,16 +348,11 @@ namespace drawinglayer::primitive2d
if(aRetval.empty())
return nullptr;
- // outline AND shadow depend on NO TextRelief (see dialog)
- const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
- const bool bHasShadow(!bHasTextRelief && getShadow());
- const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
-
- if(bHasShadow || bHasTextRelief || bHasOutline)
+ if(hasShadow() || hasTextRelief() || hasOutline())
{
Primitive2DReference aShadow;
- if(bHasShadow)
+ if(hasShadow())
{
// create shadow with current content (in aRetval). Text shadow
// is constant, relative to font size, rotated with the text and has a
@@ -364,7 +379,7 @@ namespace drawinglayer::primitive2d
Primitive2DContainer(aRetval));
}
- if(bHasTextRelief)
+ if(hasTextRelief())
{
// create emboss using an own helper primitive since this will
// be view-dependent
@@ -412,7 +427,7 @@ namespace drawinglayer::primitive2d
aTextEffectStyle2D))
};
}
- else if(bHasOutline)
+ else if(hasOutline())
{
// create outline using an own helper primitive since this will
// be view-dependent
@@ -490,6 +505,25 @@ namespace drawinglayer::primitive2d
{
}
+ bool TextDecoratedPortionPrimitive2D::hasTextRelief() const
+ {
+ return TEXT_RELIEF_NONE != getTextRelief();
+ }
+
+ bool TextDecoratedPortionPrimitive2D::hasShadow() const
+ {
+ // not allowed with TextRelief, else defined in FontAttributes
+ return !hasTextRelief() && getShadow();
+ }
+
+ bool TextDecoratedPortionPrimitive2D::hasTextDecoration() const
+ {
+ return TEXT_LINE_NONE != getFontOverline()
+ || TEXT_LINE_NONE != getFontUnderline()
+ || TEXT_STRIKEOUT_NONE != getTextStrikeout()
+ || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark();
+ }
+
bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx
index ec2d18a79204..db9a6359ccf2 100644
--- a/drawinglayer/source/primitive2d/textprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -231,6 +231,30 @@ bool LocalesAreEqual(const css::lang::Locale& rA, const css::lang::Locale& rB)
return (rA.Language == rB.Language && rA.Country == rB.Country && rA.Variant == rB.Variant);
}
+bool TextSimplePortionPrimitive2D::hasTextRelief() const
+{
+ // not possible for TextSimplePortionPrimitive2D
+ return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasShadow() const
+{
+ // not possible for TextSimplePortionPrimitive2D
+ return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasTextDecoration() const
+{
+ // not possible for TextSimplePortionPrimitive2D
+ return false;
+}
+
+bool TextSimplePortionPrimitive2D::hasOutline() const
+{
+ // not allowed with TextRelief, else defined in FontAttributes
+ return !hasTextRelief() && getFontAttribute().getOutline();
+}
+
bool TextSimplePortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
{
if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index a212ac270906..083f6ab51cde 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -3030,38 +3030,14 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
pSalLayout->GetTextWidth());
}
- // prepare flags that are only needed if Text is Decorated
- bool bHasTextRelief(false);
- bool bHasShadow(false);
- bool bHasTextDecoration(false);
-
- if (nullptr != pDecoratedCandidate)
- {
- // outline AND shadow depend on NO TextRelief (see dialog)
- bHasTextRelief = primitive2d::TEXT_RELIEF_NONE != pDecoratedCandidate->getTextRelief();
- bHasShadow = !bHasTextRelief && pDecoratedCandidate->getShadow();
-
- // check if TextDecoration is needed
- bHasTextDecoration
- = primitive2d::TEXT_LINE_NONE != pDecoratedCandidate->getFontOverline()
- || primitive2d::TEXT_LINE_NONE != pDecoratedCandidate->getFontUnderline()
- || primitive2d::TEXT_STRIKEOUT_NONE != pDecoratedCandidate->getTextStrikeout()
- || primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
- != pDecoratedCandidate->getTextEmphasisMark();
- }
-
- // prepare flags for non-decorated after that, these *might* be
- // dependent on flags above
- bool bHasOutline(!bHasTextRelief && rTextCandidate.getFontAttribute().getOutline());
-
- if (bHasShadow)
+ if (rTextCandidate.hasShadow())
{
// Text shadow is constant, relative to font size, *not* rotated with
// text (always from top-left!)
static const double fFactor(1.0 / 24.0);
const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
- // see OutputDevice::ImplDrawSpecialText -> no longer simple fixed color
+ // see ::ImplDrawSpecialText -> no longer simple fixed color
const basegfx::BColor aBlack(0.0, 0.0, 0.0);
basegfx::BColor aShadowColor(aBlack);
if (aBlack == rTextCandidate.getFontColor()
@@ -3084,7 +3060,7 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
renderSalLayout(pSalLayout, aShadowColor, aShadowFullTextTransform,
getViewInformation2D().getUseAntiAliasing());
- if (bHasTextDecoration)
+ if (rTextCandidate.hasTextDecoration())
{
const basegfx::B2DHomMatrix aTransform(getViewInformation2D().getObjectTransformation()
* aShadowTransform);
@@ -3092,16 +3068,13 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
&aTransform, &aShadowColor);
}
}
+ // get TextColor early, may have to be modified
+ basegfx::BColor aTextColor(rTextCandidate.getFontColor());
- if (bHasTextRelief)
- {
- // todo
- }
- else if (bHasOutline)
+ if (rTextCandidate.hasOutline())
{
// render as outline
- basegfx::BColor aTextColor(
- maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
+ aTextColor = maBColorModifierStack.getModifiedColor(aTextColor);
basegfx::B2DHomMatrix aInvViewTransform;
// discrete offsets defined here to easily allow to change them,
@@ -3117,7 +3090,7 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
std::pair<double, double>{ fZero, fPlus }, std::pair<double, double>{ fPlus, fPlus }
};
- if (bHasTextDecoration)
+ if (rTextCandidate.hasTextDecoration())
{
// to use discrete offset (pixels) we will need the back-transform from
// discrete view coordinates to 'world' coordinates (logic view coordinates),
@@ -3137,7 +3110,7 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
basegfx::utils::createTranslateB2DHomMatrix(offset.first, offset.second));
renderSalLayout(pSalLayout, aTextColor, aDiscreteOffset * aFullTextTransform,
getViewInformation2D().getUseAntiAliasing());
- if (bHasTextDecoration)
+ if (rTextCandidate.hasTextDecoration())
{
const basegfx::B2DHomMatrix aTransform(
aInvViewTransform * aDiscreteOffset
@@ -3151,27 +3124,64 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
aTextColor = maBColorModifierStack.getModifiedColor(COL_WHITE.getBColor());
renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
getViewInformation2D().getUseAntiAliasing());
- if (bHasTextDecoration)
+ if (rTextCandidate.hasTextDecoration())
{
renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans,
nullptr, &aTextColor);
}
+
+ // paint is complete, Outline and TextRelief cannot be combined, return
+ return;
}
- else
+
+ if (rTextCandidate.hasTextRelief())
{
- // render text
- const basegfx::BColor aTextColor(
- maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
- renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+ // manipulate TextColor for final text paint below (see ::ImplDrawSpecialText)
+ if (aTextColor == COL_BLACK.getBColor())
+ aTextColor = COL_WHITE.getBColor();
+
+ // relief offset defined here to easily allow to change them
+ // see ::ImplDrawSpecialText and the coment @ 'nOff += mnDPIX/300'
+ const bool bEmboss(primitive2d::TEXT_RELIEF_EMBOSSED
+ == pDecoratedCandidate->getTextRelief());
+ constexpr double fReliefOffset(1.1);
+ const double fOffset(bEmboss ? fReliefOffset : -fReliefOffset);
+ const basegfx::B2DHomMatrix aDiscreteOffset(
+ basegfx::utils::createTranslateB2DHomMatrix(fOffset, fOffset));
+
+ // see aReliefColor in ::ImplDrawSpecialText
+ basegfx::BColor aReliefColor(COL_LIGHTGRAY.getBColor());
+ if (COL_WHITE.getBColor() == aTextColor)
+ aReliefColor = COL_BLACK.getBColor();
+ aReliefColor = maBColorModifierStack.getModifiedColor(aReliefColor);
+
+ // render relief text with offset
+ renderSalLayout(pSalLayout, aReliefColor, aDiscreteOffset * aFullTextTransform,
getViewInformation2D().getUseAntiAliasing());
- if (bHasTextDecoration)
+ if (rTextCandidate.hasTextDecoration())
{
- // render using same geometry/primitives that a decompose would
- // create -> safe to get the same visualization for both
- renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans);
+ basegfx::B2DHomMatrix aInvViewTransform(getViewInformation2D().getViewTransformation());
+ aInvViewTransform.invert();
+ const basegfx::B2DHomMatrix aTransform(
+ aInvViewTransform * aDiscreteOffset
+ * getViewInformation2D().getObjectToViewTransformation());
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans,
+ &aTransform, &aReliefColor);
}
}
+
+ // render text
+ aTextColor = maBColorModifierStack.getModifiedColor(aTextColor);
+ renderSalLayout(pSalLayout, aTextColor, aFullTextTransform,
+ getViewInformation2D().getUseAntiAliasing());
+
+ if (rTextCandidate.hasTextDecoration())
+ {
+ // render using same geometry/primitives that a decompose would
+ // create -> safe to get the same visualization for both
+ renderTextDecorationWithOptionalTransformAndColor(*pDecoratedCandidate, aDecTrans);
+ }
}
void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)