Can I create a text outline in SharpDX using a custom text renderer?

一曲冷凌霜 提交于 2019-12-12 04:08:32

问题


Background

Previously I was rendering text, and giving it a glowing outline by drawing the outline 1px to each side and then placing the text on top:

// Draw outline 1px to each side
context2D.DrawTextLayout(new Vector2(-1, 0), _textLayout, _highlightBrush, DrawTextOptions.NoSnap);
context2D.DrawTextLayout(new Vector2(0, -1), _textLayout, _highlightBrush, DrawTextOptions.NoSnap);
context2D.DrawTextLayout(new Vector2(1, 0), _textLayout, _highlightBrush, DrawTextOptions.NoSnap);
context2D.DrawTextLayout(new Vector2(0, 1), _textLayout, _highlightBrush, DrawTextOptions.NoSnap);
// Draw text on top of outline
context2D.DrawTextLayout(new Vector2(0, 0), _textLayout, _foregroundBrush, DrawTextOptions.NoSnap);

That works great. But I've realized that I need to have different parts of my string have different foreground colors and highlights.

I could have two separate TextLayout's side by side, but I'm already having to scale my font to fit the container size, doing it for two sections side by side just seemed really complex, here's my current scaling code that tries to find the best font size to fit the container:

private void CreateTextFormatAndLayout( string text )
{
    float maxFontSize = _settings.BoardLayout.ActualGradeMaxFontSize;
    float minFontSize = _settings.BoardLayout.ActualGradeMinFontSize;
    var factory = DeviceManager.DirectWriteFactory;

    SharpDX.DirectWrite.TextFormat textFormat = null;
    SharpDX.DirectWrite.TextLayout textLayout = null;

    // step backward through font sizes till we find one that fits
    for ( float fontSize = maxFontSize; fontSize > minFontSize; fontSize -= .5f ) {

        // if have leftover values from previous loop, dispose
        if ( textFormat != null )
            textFormat.Dispose();
        if ( textLayout != null )
            textLayout.Dispose();

        // generate sharpdx format and layout based on current font size
        textFormat = new SharpDX.DirectWrite.TextFormat(factory, "Arial", SharpDX.DirectWrite.FontWeight.Normal, SharpDX.DirectWrite.FontStyle.Normal, fontSize);
        textFormat.WordWrapping = SharpDX.DirectWrite.WordWrapping.NoWrap;
        textFormat.TextAlignment = SharpDX.DirectWrite.TextAlignment.Center;
        textLayout = new SharpDX.DirectWrite.TextLayout(factory, text, textFormat, _regionWidthPx, _regionWidthPx);

        // if text will fit in the container break
        if ( textLayout.Metrics.Width < _regionWidthPx )
            break;
    }

    // Set format and layout
    _textFormat = ToDispose(textFormat);
    _textLayout = ToDispose(textLayout);
}

That's when I discovered you can create custom handlers for different sections of the TextLayout. (Glyphs) I don't know very much about glyphs but this example seemed to indicate it wasn't too difficult:

Example of using custom renderer

What I'm trying to do

I want to do something similar, except I want to pass two colors my custom text renderer, and have it render the outline as well. I'm not looking for a code review, I mostly want to know if I'm on the right track. Is my MultiBrush idea totally wrong or can I do this this way? I am sure others have had to do advanced text formatting. Here's my basic idea, I can flesh it out more if needed but I think anyone familiar with SharpDX will be able to see what I'm trying to do:

public class MultiBrush : ComObject
{
    public MultiBrush( Brush foreground, Brush highlight )
    {
        ForegroundBrush = foreground;
        HighlightBrush = highlight;
    }

    public Brush ForegroundBrush;
    public Brush HighlightBrush;
}

public class CustomColorRenderer : SharpDX.DirectWrite.TextRendererBase
{
    private RenderTarget renderTarget;
    private MultiBrush defaultBrush;

    public void AssignResources( RenderTarget renderTarget, MultiBrush defaultBrush )
    {
        this.renderTarget = renderTarget;
        this.defaultBrush = defaultBrush;
    }

    public override Result DrawGlyphRun( object clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, ComObject clientDrawingEffect )
    {
        MultiBrush sb = defaultBrush;
        if ( clientDrawingEffect != null && clientDrawingEffect is MultiBrush ) {
            sb = (MultiBrush)clientDrawingEffect;
        }

        try {
            // render shadow 1 px away
            this.renderTarget.DrawGlyphRun(new Vector2(baselineOriginX + 1, baselineOriginY + 1), glyphRun, sb.HighlightBrush, measuringMode);
            // render main text
            this.renderTarget.DrawGlyphRun(new Vector2(baselineOriginX, baselineOriginY), glyphRun, sb.ForegroundBrush, measuringMode);
            return Result.Ok;
        }
        catch {
            return Result.Fail;
        }
    }
}
...

CustomColorRenderer customRenderer = new CustomColorRenderer();
textLayout = new TextLayout(dwFactory, introText, textFormat, 300.0f, 200.0f);

SolidColorBrush brush1 = new SolidColorBrush(DeviceManager.Direct2DContext, _foregroundColor);
SolidColorBrush brush2 = new SolidColorBrush(DeviceManager.Direct2DContext, _highlightColor);
multiBrush = new MultiBrush(brush1, brush2);

// Apply custom modification to text
textLayout.SetDrawingEffect(multiBrush, new TextRange(10, 20));
// Render custom text
textLayout.Draw(customRenderer, 0, 0);

来源:https://stackoverflow.com/questions/37311969/can-i-create-a-text-outline-in-sharpdx-using-a-custom-text-renderer

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!