Exemple #1
0
        protected void ShapeGlyphRun(TextAnalyzer textAnalyzer, int runIndex, ref int glyphStart)
        {
            // Shapes a single run of text into glyphs.
            // Alternately, you could iteratively interleave shaping and line
            // breaking to reduce the number glyphs held onto at once. It's simpler
            // for this demostration to just do shaping and line breaking as two
            // separate processes, but realize that this does have the consequence that
            // certain advanced fonts containing line specific features (like Gabriola)
            // will shape as if the line is not broken.

            Run run = runs_[runIndex];
            int textStart = run.textStart;
            int textLength = run.textLength;
            int maxGlyphCount = glyphIndices_.Length - glyphStart;
            int actualGlyphCount = 0;

            run.glyphStart = glyphStart;
            run.glyphCount = 0;

            if (textLength == 0)
                return;// Nothing to do..

            // Allocate space for shaping to fill with glyphs and other information,
            // with about as many glyphs as there are text characters. We'll actually
            // need more glyphs than codepoints if they are decomposed into separate
            // glyphs, or fewer glyphs than codepoints if multiple are substituted
            // into a single glyph. In any case, the shaping process will need some
            // room to apply those rules to even make that determintation.

            if (textLength > maxGlyphCount)
            {
                maxGlyphCount = EstimateGlyphCount(textLength);
                int totalGlyphsArrayCount = glyphStart + maxGlyphCount;
                short[] Resized_glyphIndices_ = new short[totalGlyphsArrayCount];
                glyphIndices_.CopyTo(Resized_glyphIndices_, 0);
                glyphIndices_ = Resized_glyphIndices_;
            }


            ShapingTextProperties[] textProps = new ShapingTextProperties[textLength];
            ShapingGlyphProperties[] glyphProps = new ShapingGlyphProperties[maxGlyphCount];

            // Get the glyphs from the text, retrying if needed.
            int tries = 0;
            while (tries < 2)// We'll give it two chances.
            {
                short[] call_glyphClusters_ = new short[glyphClusters_.Length - textStart];
                short[] call_glyphIndices_ = new short[glyphIndices_.Length - glyphStart];

                bool isDone = false;
                try
                {
                    textAnalyzer.GetGlyphs(
                        text_.Substring(textStart, textLength),
                        textLength,
                        fontFace_,
                        run.isSideways,
                        (run.bidiLevel % 2 == 1),
                        run.script,
                        localName_,
                        run.isNumberSubstituted ? numberSubstitution_ : null,
                        null,
                        null,
                        maxGlyphCount,
                        call_glyphClusters_,
                        textProps,
                        call_glyphIndices_,
                        glyphProps,
                        out actualGlyphCount);
                    Array.Copy(call_glyphClusters_, 0, glyphClusters_, textStart, call_glyphClusters_.Length);
                    Array.Copy(call_glyphIndices_, 0, glyphIndices_, glyphStart, call_glyphIndices_.Length);
                    isDone = true;
                }
                finally
                {
                    tries++;
                    // Try again using a larger buffer.
                    maxGlyphCount = EstimateGlyphCount(maxGlyphCount);
                    int totalGlyphsArrayCount = glyphStart + maxGlyphCount;

                    glyphProps = new ShapingGlyphProperties[maxGlyphCount];
                    glyphIndices_ = new short[totalGlyphsArrayCount];
                }
                if (isDone)
                    break;
            }

            // Get the placement of the all the glyphs.
            if (glyphAdvances_.Length < glyphStart + actualGlyphCount)
            {
                float[] Resized_glyphAdvances_ = new float[glyphStart + actualGlyphCount];
                glyphAdvances_.CopyTo(Resized_glyphAdvances_, 0);
                glyphAdvances_ = Resized_glyphAdvances_;
            }
            if (glyphOffsets_.Length < glyphStart + actualGlyphCount)
            {
                GlyphOffset[] Resized_glyphOffsets_ = new GlyphOffset[glyphStart + actualGlyphCount];
                glyphOffsets_.CopyTo(Resized_glyphOffsets_, 0);
                glyphOffsets_ = Resized_glyphOffsets_;
            }

            short[] call2_glyphClusters_ = new short[glyphClusters_.Length - textStart];
            Array.Copy(glyphClusters_, textStart, call2_glyphClusters_, 0, call2_glyphClusters_.Length);
            short[] call2_glyphIndices_ = new short[glyphIndices_.Length - glyphStart];
            Array.Copy(glyphIndices_, glyphStart, call2_glyphIndices_, 0, call2_glyphIndices_.Length);
            float[] call2_glyphAdvances_ = new float[glyphAdvances_.Length - glyphStart];
            Array.Copy(glyphAdvances_, glyphStart, call2_glyphAdvances_, 0, call2_glyphAdvances_.Length);
            GlyphOffset[] call2_glyphOffsets_ = new GlyphOffset[glyphOffsets_.Length - glyphStart];
            Array.Copy(glyphOffsets_, glyphStart, call2_glyphOffsets_, 0, call2_glyphOffsets_.Length);

            textAnalyzer.GetGlyphPlacements(
                text_.Substring(textStart, textLength),
                call2_glyphClusters_,
                textProps,
                textLength,
                call2_glyphIndices_,
                glyphProps,
                actualGlyphCount,
                fontFace_,
                fontEmSize_,
                run.isSideways,
                run.bidiLevel % 2 == 1,
                run.script,
                localName_,
                null,
                null,
                call2_glyphAdvances_,
                call2_glyphOffsets_);
            //call2_glyphClusters_.CopyTo(glyphClusters_, textStart);
            call2_glyphAdvances_.CopyTo(glyphAdvances_, glyphStart);
            call2_glyphOffsets_.CopyTo(glyphOffsets_, glyphStart);

            // Certain fonts, like Batang, contain glyphs for hidden control
            // and formatting characters. So we'll want to explicitly force their
            // advance to zero.
            if (run.script.Shapes == ScriptShapes.NoVisual)
            {
                for (int i = glyphStart; i < glyphStart + actualGlyphCount; i++)
                    glyphAdvances_[i] = 0;
            }

            // Set the final glyph count of this run and advance the starting glyph.
            run.glyphCount = actualGlyphCount;
            runs_[runIndex] = run;
            glyphStart += actualGlyphCount;
        }
Exemple #2
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);
        }