diff options
author | Armin Le Grand (Collabora) <Armin.Le.Grand@me.com> | 2024-08-15 11:29:28 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2024-08-15 16:05:41 +0200 |
commit | 6ab0c616d72f224c5de87b8afd2b228c223bb056 (patch) | |
tree | 7529792586e21cf6cb6bb63fe2aae4ac39ff43dc /drawinglayer/source | |
parent | 0e4266dd904ffb79c84f6b281bd822460cc64fde (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.cxx | 10 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 149 |
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); + } } } |