Ejemplo n.º 1
0
        /// <summary>
        /// Allocates the features from the jagged array..
        /// </summary>
        /// <param name="features">The features.</param>
        /// <returns>A pointer to the allocated native features or 0 if features is null or empty.</returns>
        private static IntPtr AllocateFeatures(FontFeature[][] features)
        {
            unsafe
            {
                var pFeatures = (byte*)0;

                if (features != null && features.Length > 0)
                {

                    // Calculate the total size of the buffer to allocate:
                    //      (0)              (1)                    (2)
                    // -------------------------------------------------------------
                    // |   array    | TypographicFeatures ||   FontFeatures        ||
                    // | ptr to (1) |     |       |       ||                       ||
                    // |            | ptr to FontFeatures ||                       ||
                    // -------------------------------------------------------------
                    // Offset in bytes to (1)
                    int offsetToTypographicFeatures = sizeof(IntPtr) * features.Length;
                    
                    // Add offset (1) and Size in bytes to (1)
                    int calcSize = offsetToTypographicFeatures + sizeof(TypographicFeatures) * features.Length;

                    // Calculate size (2)
                    foreach (var fontFeature in features)
                    {
                        if (fontFeature == null) 
                            throw new ArgumentNullException("FontFeature[] inside features array cannot be null", "features");

                        // calcSize += typographicFeatures.Length * sizeof(FontFeature)
                        calcSize += sizeof(FontFeature) * fontFeature.Length;
                    }

                    // Allocate the whole buffer
                    pFeatures = (byte*)Marshal.AllocHGlobal(calcSize);

                    // Pointer to (1)
                    var pTypographicFeatures = (TypographicFeatures*)(pFeatures + offsetToTypographicFeatures);

                    // Pointer to (2)
                    var pFontFeatures = (FontFeature*)(pTypographicFeatures + features.Length);

                    // Iterate on features and copy them to (2)
                    for (int i = 0; i < features.Length; i++)
                    {
                        // Write array pointers in (0)
                        ((void**)pFeatures)[i] = pTypographicFeatures;
                        
                        var featureSet = features[i];

                        // Write TypographicFeatures in (1)
                        pTypographicFeatures->Features = (IntPtr)pFontFeatures;
                        pTypographicFeatures->FeatureCount = featureSet.Length;
                        pTypographicFeatures++;

                        // Write FontFeatures in (2)
                        for (int j = 0; j < featureSet.Length; j++)
                        {
                            *pFontFeatures = featureSet[j];
                            pFontFeatures++;
                        }
                    }
                }
                return (IntPtr)pFeatures;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Gets the glyphs (TODO doc)
        /// </summary>
        /// <param name="textString">The text string.</param>
        /// <param name="textLength">Length of the text.</param>
        /// <param name="fontFace">The font face.</param>
        /// <param name="isSideways">if set to <c>true</c> [is sideways].</param>
        /// <param name="isRightToLeft">if set to <c>true</c> [is right to left].</param>
        /// <param name="scriptAnalysis">The script analysis.</param>
        /// <param name="localeName">Name of the locale.</param>
        /// <param name="numberSubstitution">The number substitution.</param>
        /// <param name="features">The features.</param>
        /// <param name="featureRangeLengths">The feature range lengths.</param>
        /// <param name="maxGlyphCount">The max glyph count.</param>
        /// <param name="clusterMap">The cluster map.</param>
        /// <param name="textProps">The text props.</param>
        /// <param name="glyphIndices">The glyph indices.</param>
        /// <param name="glyphProps">The glyph props.</param>
        /// <param name="actualGlyphCount">The actual glyph count.</param>
        /// <returns>
        /// If the method succeeds, it returns <see cref="Result.Ok"/>.
        /// </returns>
        /// <unmanaged>HRESULT IDWriteTextAnalyzer::GetGlyphs([In, Buffer] const wchar_t* textString,[In] unsigned int textLength,[In] IDWriteFontFace* fontFace,[In] BOOL isSideways,[In] BOOL isRightToLeft,[In] const DWRITE_SCRIPT_ANALYSIS* scriptAnalysis,[In, Buffer, Optional] const wchar_t* localeName,[In, Optional] IDWriteNumberSubstitution* numberSubstitution,[In, Optional] const void** features,[In, Buffer, Optional] const unsigned int* featureRangeLengths,[In] unsigned int featureRanges,[In] unsigned int maxGlyphCount,[Out, Buffer] unsigned short* clusterMap,[Out, Buffer] DWRITE_SHAPING_TEXT_PROPERTIES* textProps,[Out, Buffer] unsigned short* glyphIndices,[Out, Buffer] DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProps,[Out] unsigned int* actualGlyphCount)</unmanaged>
        public void GetGlyphs(string textString, int textLength, SharpDX.DirectWrite.FontFace fontFace, bool isSideways, bool isRightToLeft, SharpDX.DirectWrite.ScriptAnalysis scriptAnalysis, string localeName, SharpDX.DirectWrite.NumberSubstitution numberSubstitution, FontFeature[][] features, int[] featureRangeLengths, int maxGlyphCount, short[] clusterMap, SharpDX.DirectWrite.ShapingTextProperties[] textProps, short[] glyphIndices, SharpDX.DirectWrite.ShapingGlyphProperties[] glyphProps, out int actualGlyphCount)
        {

            var pFeatures = AllocateFeatures(features);
            try
            {
                GetGlyphs(
                    textString,
                    textLength,
                    fontFace,
                    isSideways,
                    isRightToLeft,
                    scriptAnalysis,
                    localeName,
                    numberSubstitution,
                    pFeatures,
                    featureRangeLengths,
                    featureRangeLengths == null ? 0 : featureRangeLengths.Length,
                    maxGlyphCount,
                    clusterMap,
                    textProps,
                    glyphIndices,
                    glyphProps,
                    out actualGlyphCount);
            } finally
            {
                if (pFeatures != IntPtr.Zero) Marshal.FreeHGlobal(pFeatures);
            }
        }
Ejemplo n.º 3
0
 /// <summary>
 /// Gets the GDI compatible glyph placements.
 /// </summary>
 /// <param name="textString">The text string.</param>
 /// <param name="clusterMap">The cluster map.</param>
 /// <param name="textProps">The text props.</param>
 /// <param name="textLength">Length of the text.</param>
 /// <param name="glyphIndices">The glyph indices.</param>
 /// <param name="glyphProps">The glyph props.</param>
 /// <param name="glyphCount">The glyph count.</param>
 /// <param name="fontFace">The font face.</param>
 /// <param name="fontEmSize">Size of the font em.</param>
 /// <param name="pixelsPerDip">The pixels per dip.</param>
 /// <param name="transform">The transform.</param>
 /// <param name="useGdiNatural">if set to <c>true</c> [use GDI natural].</param>
 /// <param name="isSideways">if set to <c>true</c> [is sideways].</param>
 /// <param name="isRightToLeft">if set to <c>true</c> [is right to left].</param>
 /// <param name="scriptAnalysis">The script analysis.</param>
 /// <param name="localeName">Name of the locale.</param>
 /// <param name="features">The features.</param>
 /// <param name="featureRangeLengths">The feature range lengths.</param>
 /// <param name="glyphAdvances">The glyph advances.</param>
 /// <param name="glyphOffsets">The glyph offsets.</param>
 /// <returns>
 /// If the method succeeds, it returns <see cref="Result.Ok"/>.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::GetGdiCompatibleGlyphPlacements([In, Buffer] const wchar_t* textString,[In, Buffer] const unsigned short* clusterMap,[In, Buffer] DWRITE_SHAPING_TEXT_PROPERTIES* textProps,[In] unsigned int textLength,[In, Buffer] const unsigned short* glyphIndices,[In, Buffer] const DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProps,[In] unsigned int glyphCount,[In] IDWriteFontFace* fontFace,[In] float fontEmSize,[In] float pixelsPerDip,[In, Optional] const DWRITE_MATRIX* transform,[In] BOOL useGdiNatural,[In] BOOL isSideways,[In] BOOL isRightToLeft,[In] const DWRITE_SCRIPT_ANALYSIS* scriptAnalysis,[In, Buffer, Optional] const wchar_t* localeName,[In, Optional] const void** features,[In, Buffer, Optional] const unsigned int* featureRangeLengths,[In] unsigned int featureRanges,[Out, Buffer] float* glyphAdvances,[Out, Buffer] DWRITE_GLYPH_OFFSET* glyphOffsets)</unmanaged>
 public void GetGdiCompatibleGlyphPlacements(string textString, short[] clusterMap, SharpDX.DirectWrite.ShapingTextProperties[] textProps, int textLength, short[] glyphIndices, SharpDX.DirectWrite.ShapingGlyphProperties[] glyphProps, int glyphCount, SharpDX.DirectWrite.FontFace fontFace, float fontEmSize, float pixelsPerDip, SharpDX.DirectWrite.Matrix? transform, bool useGdiNatural, bool isSideways, bool isRightToLeft, SharpDX.DirectWrite.ScriptAnalysis scriptAnalysis, string localeName, FontFeature[][] features, int[] featureRangeLengths, float[] glyphAdvances, SharpDX.DirectWrite.GlyphOffset[] glyphOffsets)
 {
     var pFeatures = AllocateFeatures(features);
     try
     {
         GetGdiCompatibleGlyphPlacements(
             textString,
             clusterMap,
             textProps,
             textLength,
             glyphIndices,
             glyphProps,
             glyphCount,
             fontFace,
             fontEmSize,
             pixelsPerDip,
             transform,
             useGdiNatural,
             isSideways,
             isRightToLeft,
             scriptAnalysis,
             localeName,
             pFeatures,
             featureRangeLengths,
             featureRangeLengths == null ? 0 : featureRangeLengths.Length,
             glyphAdvances,
             glyphOffsets
             );
     }
     finally
     {
         if (pFeatures != IntPtr.Zero) Marshal.FreeHGlobal(pFeatures);
     }
 }
Ejemplo n.º 4
0
        /// <inheritdoc />
        protected override void Draw()
        {
            while (this.pendingActions.TryDequeue(out var action))
            {
                action();
            }

            var args = this.neovimClient.GetScreen();

            if (args == null)
            {
                return;
            }

            this.scriptAnalysesCache.StartNewFrame();
            this.DeviceContext.BeginDraw();
            this.DeviceContext.Clear(Helpers.GetColor(args.BackgroundColor, 0));

            // Paint the background
            for (int i = 0; i < args.Cells.GetLength(0); i++)
            {
                for (int j = 0; j < args.Cells.GetLength(1); j++)
                {
                    if (args.Cells[i, j].BackgroundColor != args.BackgroundColor || args.Cells[i, j].Reverse)
                    {
                        var x = j * this.textParam.CharWidth;
                        var y = i * this.textParam.LineHeight;

                        var rect  = new RawRectangleF(x, y, x + this.textParam.CharWidth, y + this.textParam.LineHeight);
                        int color = args.Cells[i, j].Reverse ? args.Cells[i, j].ForegroundColor : args.Cells[i, j].BackgroundColor;
                        var brush = this.brushCache.GetBrush(this.DeviceContext, color);
                        this.DeviceContext.FillRectangle(rect, brush);
                    }
                }
            }

            // Paint the foreground
            for (int i = 0; i < args.Cells.GetLength(0); i++)
            {
                int j = 0;

                while (j < args.Cells.GetLength(1))
                {
                    // Cells with the same style should be analyzed together.
                    // This prevents the inproper ligature in <html>=
                    // Of course, it relies on enabling the syntax.
                    int  cellRangeStart = j;
                    int  cellRangeEnd   = j;
                    Cell startCell      = args.Cells[i, cellRangeStart];
                    while (true)
                    {
                        if (cellRangeEnd == args.Cells.GetLength(1))
                        {
                            break;
                        }

                        Cell cell = args.Cells[i, cellRangeEnd];
                        if (cell.Character != null &&
                            (cell.ForegroundColor != startCell.ForegroundColor ||
                             cell.BackgroundColor != startCell.BackgroundColor ||
                             cell.SpecialColor != startCell.SpecialColor ||
                             cell.Italic != startCell.Italic ||
                             cell.Bold != startCell.Bold ||
                             cell.Reverse != startCell.Reverse ||
                             cell.Undercurl != startCell.Undercurl ||
                             cell.Underline != startCell.Underline))
                        {
                            break;
                        }

                        cellRangeEnd++;
                    }

                    j = cellRangeEnd;

                    var fontWeight = args.Cells[i, cellRangeStart].Bold ? DWrite.FontWeight.Bold : this.textParam.Weight;
                    var fontStyle  = args.Cells[i, cellRangeStart].Italic ? DWrite.FontStyle.Italic : this.textParam.Style;

                    int cellIndex = cellRangeStart;
                    using (var textSource = new RowTextSource(this.factoryDWrite, args.Cells, i, cellRangeStart, cellRangeEnd))
                    {
                        var scriptAnalyses = this.scriptAnalysesCache.GetOrAddAnalysisResult(
                            textSource.GetTextAtPosition(0),
                            (_) =>
                        {
                            using (var textSink = new TextAnalysisSink())
                            {
                                this.textAnalyzer.AnalyzeScript(textSource, 0, textSource.Length, textSink);
                                return(textSink.ScriptAnalyses);
                            }
                        });

                        // The result of AalyzeScript may cut the text into several ranges,
                        // and in each range the text's scripts are different.
                        foreach (var(codePointStart, codePointLength, scriptAnalysis) in scriptAnalyses)
                        {
                            var     glyphBufferLength = (codePointLength * 3 / 2) + 16;
                            var     clusterMap        = new short[codePointLength];
                            var     textProperties    = new DWrite.ShapingTextProperties[codePointLength];
                            short[] indices;
                            DWrite.ShapingGlyphProperties[] shapingProperties;
                            var fontFace = this.fontCache.GetPrimaryFontFace(fontWeight, fontStyle);
                            int actualGlyphCount;

                            // We don't know how many glyphs the text have. TextLength * 3 / 2 + 16
                            // is an empirical estimation suggested by MSDN. So using a loop to detect
                            // the actual glyph count.
                            while (true)
                            {
                                indices           = new short[glyphBufferLength];
                                shapingProperties = new DWrite.ShapingGlyphProperties[glyphBufferLength];
                                try
                                {
                                    var str = textSource.GetSubString(codePointStart, codePointLength);

                                    DWrite.FontFeature[][] fontFeatures = null;
                                    int[] featureLength = null;

                                    if (!this.EnableLigature)
                                    {
                                        fontFeatures = new DWrite.FontFeature[][]
                                        {
                                            new DWrite.FontFeature[]
                                            {
                                                new DWrite.FontFeature(DWrite.FontFeatureTag.StandardLigatures, 0),
                                            },
                                        };

                                        featureLength = new int[]
                                        {
                                            str.Length,
                                        };
                                    }

                                    this.textAnalyzer.GetGlyphs(
                                        str,
                                        str.Length,
                                        fontFace,
                                        false,
                                        false,
                                        scriptAnalysis,
                                        null,
                                        null,
                                        fontFeatures,
                                        featureLength,
                                        glyphBufferLength,
                                        clusterMap,
                                        textProperties,
                                        indices,
                                        shapingProperties,
                                        out actualGlyphCount);
                                    break;
                                }
                                catch (SharpDX.SharpDXException e)
                                {
                                    const int ERROR_INSUFFICIENT_BUFFER = 122;
                                    if (e.ResultCode == SharpDX.Result.GetResultFromWin32Error(ERROR_INSUFFICIENT_BUFFER))
                                    {
                                        glyphBufferLength *= 2;
                                    }
                                }
                            }

                            for (int codePointIndex = 0, glyphIndex = 0; codePointIndex < codePointLength;)
                            {
                                // var fontWeight = args.Screen[i, cellIndex].Bold ? DWrite.FontWeight.Bold : DWrite.FontWeight.Normal;
                                // var fontStyle = args.Screen[i, cellIndex].Italic ? DWrite.FontStyle.Italic : DWrite.FontStyle.Normal;
                                var     foregroundColor = args.Cells[i, cellIndex].Reverse ? args.Cells[i, cellIndex].BackgroundColor : args.Cells[i, cellIndex].ForegroundColor;
                                var     foregroundBrush = this.brushCache.GetBrush(this.DeviceContext, foregroundColor);
                                var     fontFace2       = fontFace;
                                short[] indices2;

                                int cellWidth      = 0;
                                int codePointCount = 0;
                                int glyphCount     = 0;

                                if (indices[glyphIndex] == 0)
                                {
                                    // If the primary font doesn't have the glyph, get a font from system font fallback.
                                    // Ligatures for fallback fonts are not supported yet.
                                    int codePoint = args.Cells[i, cellIndex].Character.Value;
                                    fontFace2  = this.fontCache.GetFontFace(codePoint, fontWeight, fontStyle);
                                    indices2   = fontFace2.GetGlyphIndices(new int[] { codePoint });
                                    glyphCount = indices2.Length;

                                    // NativeInterop.Methods.wcwidth(textSource.GetCodePoint(codePointStart + codePointIndex));
                                    cellWidth      = this.GetCharWidth(args.Cells, i, cellIndex);
                                    codePointCount = 1;
                                }
                                else
                                {
                                    // The cluster map stores the information about the codepoint-glyph mapping.
                                    // If several codepoints share the same glyph (e.g. ligature), then they will
                                    // have the same value in clusterMap.
                                    // If one code point has several corresponding glyphs, then for the next codepoint,
                                    // the value in clusterMap will bump higher.
                                    // Example:  CodePointLength = 5, GlyphCount = 5, clusterMap = [0, 1, 1, 2, 4] means:
                                    // Codepoint Index    Glyph Index
                                    //       0   -----------   0
                                    //       1   -----------   1
                                    //       2   ----------/
                                    //       3   -----------   2
                                    //           \----------   3
                                    //       4   -----------   4
                                    var cluster     = clusterMap[codePointIndex];
                                    int nextCluster = cluster;
                                    while (true)
                                    {
                                        if (codePointIndex + codePointCount == clusterMap.Length)
                                        {
                                            nextCluster++;
                                            break;
                                        }

                                        nextCluster = clusterMap[codePointIndex + codePointCount];
                                        if (cluster != nextCluster)
                                        {
                                            break;
                                        }

                                        // NativeInterop.Methods.wcwidth(textSource.GetCodePoint(codePointStart + codePointIndex + codePointCount));
                                        cellWidth += this.GetCharWidth(args.Cells, i, cellIndex + cellWidth);
                                        codePointCount++;
                                    }

                                    glyphCount = nextCluster - cluster;
                                    indices2   = new short[glyphCount];
                                    for (int c = 0; c < glyphCount; c++)
                                    {
                                        indices2[c] = indices[glyphIndex];
                                    }
                                }

                                using (var glyphrun = new DWrite.GlyphRun
                                {
                                    FontFace = fontFace2,
                                    Advances = null,
                                    BidiLevel = 0,
                                    FontSize = this.textParam.DipSize,
                                    Indices = indices2,
                                    IsSideways = false,
                                    Offsets = null,
                                })
                                {
                                    var origin = new RawVector2(this.textParam.CharWidth * cellIndex, this.textParam.LineHeight * (i + 0.8f));
                                    this.DeviceContext.DrawGlyphRun(origin, glyphrun, foregroundBrush, D2D.MeasuringMode.GdiNatural);
                                    glyphrun.FontFace = null;
                                }

                                cellIndex      += cellWidth;
                                codePointIndex += codePointCount;
                                glyphIndex     += glyphCount;
                            }
                        }
                    }
                }
            }

            this.DeviceContext.EndDraw();
            this.DrawCursor(args);
        }