2

I would like to draw a text exactly in the place of the mouse cursor. Because I need very high performance, I would like to use GlyphRun. Everything works almost well, but unfortunately my text is slightly below the cursor.

Can someone help me modify this method to eliminate this vertical shift?

Now it looks like this

My expectation (the text touches the cursor)

My code:

void MyDrawer_MouseMove(object sender, MouseEventArgs e)
{
    Test1();            
}

void Test1()
{
    MyDrawer.DeleteVisual(Dv);
    MyDrawer.Cursor = Cursors.Cross;
    string text = "Hello Word";
    double size = 40;
    Dv = new DrawingVisual();
    using (var dc = Dv.RenderOpen())
    {
        Typeface typeface = new Typeface("Arial");
        if (typeface.TryGetGlyphTypeface(out GlyphTypeface glyphTypeface))
        {
            ushort[] glyphIndexes = new ushort[text.Length];
            double[] advanceWidths = new double[text.Length];

            for (int i = 0; i < text.Length; i++)
            {
                ushort glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]];
                glyphIndexes[i] = glyphIndex;
                double width = glyphTypeface.AdvanceWidths[glyphIndex] * size;
                advanceWidths[i] = width;
            }

            Point origin = Mouse.GetPosition(MyDrawer);
            //Move text belowe the cursor
            origin = new Point { X = origin.X, Y = origin.Y + (glyphTypeface.Baseline * size) };
            GlyphRun glyphRun = new GlyphRun(glyphTypeface, 0, false, size,
            glyphIndexes, origin, advanceWidths, null, null, null, null,
            null, null);

            dc.DrawGlyphRun(Brushes.Red, glyphRun);
            MyDrawer.AddVisual(Dv);
        }
    }
}

Of course, this is only a test, in practice it will not affect the cursor, but the indicated point and text will be much more than in this example.

gmik
  • 31
  • 5
  • What stops you from subtracting an appropriate amount from origin.Y? And if you really need "very high performance" do not create a new DrawingVisual each time. Instead reuse the exiting one. – Clemens Jun 12 '18 at 13:46
  • I can not determine where this shift is taking place, so it's hard for me to cut off the unknown value. Maybe this link will help someone solve my problem: [link](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/media/glyph-example.png) – gmik Jun 12 '18 at 13:54
  • Yes, the question is what is the **"appropriate amount"** to be cut off:( – gmik Jun 12 '18 at 13:57
  • It's certainly one of the values provided by GlyphTypeface, e.g. its Baseline. – Clemens Jun 12 '18 at 14:02
  • I think so, but I have already applied this property: Y = origin.Y + (glyphTypeface.Baseline * size)` – gmik Jun 12 '18 at 14:06
  • Why do you use a fixed size and not calculate with AdvanceHeights just like you do AdvanceWidths? – Joel Lucsy Jun 12 '18 at 15:30
  • I doubt that all this is necessary at all. A FormattedText should do as well. – Clemens Jun 12 '18 at 17:25
  • I would also like FormattedText, but if you compare the times of FormattedText and GlyphRun, you will understand that FormattedText is too slow for very high performance. Of course, my method is only a test, and in practice the text will be much more and will be scattered in various places. Believe me, I would not struggle with Glyphs if I could use TextBlock, FormattedText, etc – gmik Jun 13 '18 at 04:33

2 Answers2

3

I bumped into this question in search for the same issue.

Glyph content (GlyphRun for that matter) is wrapped in a black-box.
The black-box contain a bit of padding on the top.

Like so : Rought skecth of the glyph run

// To get the box size
var blackBoxHeight = glyphTypeface.Height * fontSize;

// To get the actual character height
var characterHeight = glyphTypeface.Baseline * fontSize;

// To get the padding inside the black box
var blackBoxPadding = blackBoxHeight - characterHeight;

// set the point to draw a little bit up (skip the padding)
origin.Y -= blackBoxPadding;

// Create the glyph
var run = new GlyphRun
            (
                glyphTypeface: glyphTypeface,
                bidiLevel: 0,
                isSideways: false,
                renderingEmSize: fontSize,
                pixelsPerDip: 1.0f,
                glyphIndices: indices,
                baselineOrigin: origin, /* The point containing the padding offset */
                advanceWidths: widths,
                glyphOffsets: null,
                characters: null,
                deviceFontName: null,
                clusterMap: null,
                caretStops: null,
                language: null
            );
Br4infreze
  • 342
  • 3
  • 11
  • This is not fully correct. And the calculations are quite confusing in fact. Difference between baseline height and height is padding between the base line and the bottom of the glyph cell. It just happened to be close to the padding in the top. See my other answer for details. – sasha_gud Nov 09 '22 at 09:00
0

The other answer lacks of some details. There is actually no magic or unreasoned paddings. The picture shows relations of glyphTypeface height parameters to the displayed GlyphRun.

Glyph run structure

The code that calculates corresponding line vertical positions based on the GlyphRun origin Y is shown below:

    var baseLine = origin.Y;
    var boxTop = baseLine - glyphTypeface.Baseline * FontSize;
    var boxBottom = boxTop + glyphTypeface.Height * FontSize;
    var capsHeight = baseLine - glyphTypeface.CapsHeight * FontSize;
    var lowHeight = baseLine - glyphTypeface.XHeight * FontSize;
sasha_gud
  • 1,635
  • 13
  • 18