NGUI-UILabel源碼解析

  1. UILabel
    1.1 概述
    UILabel是NGUI中用于文字顯示的組件,支持圖文顯示,富文本等功能。閱讀本文前請熟悉下NGUI的基本用法和基本屬性。
    1.2 基本用法


    UILabel1.png
UILabel2.png

1.3 基礎屬性
溢出類型
效果類型
字體類型
對齊方式
圖文樣式
漸變
邊距
支持富文本
多行和最高行數(shù)

1.4 基本原理

  • 字體方案有兩種:Unity的動態(tài)字體(trueTypeFont,TTF)和NGUI的BitmapFont(BMFont
  • UILabel維護mShouldBeProcessed的bool變量,在設置基本屬性的時候都會將mShouldBeProcessed設置為true,并重寫父類UIWidget的屬性drawingDimensions、localCorners、worldCorners、localSize和方法GetSides中判斷shouldBeProcessed是否為true,為true則調(diào)用UILabel的ProcessText重新計算信息并重新繪制。
  • NGUIText作為輔助類,是一個靜態(tài)類,由于繪制文字的參數(shù)很多,由其參數(shù)緩存和相應的計算方法,減少相應的內(nèi)存占用。
  1. UILabel核心方法
    2.1 ProcessText
    2.1.1 流程
  • 計算UILabel設定的矩形大小和真正顯示的矩形大小,其設定矩形大小還受OverFlow類型的影響,如類型為Overflow.ResizeFreely或者Overflow.ResizeHeight相應的設定大小不受當前width height的限制。
  • 調(diào)用NGUIText.Update(false):更新最終字形的大小、字符間隔finalSpacingX、 一行高度finalLineHeight和是否用符文等信息。
  • 啟用for循環(huán)不斷遞減字體大小
    • 調(diào)用NGUIText.WrapText(mText, out mProcessedText, true);
      • 根據(jù)regionHeight、finalLineHeight和maxLines計算出Label最多顯示的行數(shù);不斷遍歷字符,解析BBCode跳過用于富文本設置的字符,區(qū)分普通字符和圖文字符分別計算其字形大小glyphWidth,維護每一行剩余的寬度remainingWidth,當寬度不足的時候就換行,最后返回Label是否容納全部文本,并得到最終顯示的文本finalText。
  • 調(diào)用NGUIText.CalculatePrintedSize(mProcessedText):
    • 根據(jù)顯示的文本mProcessedText和字符間隔finalSpacingX、 一行高度finalLineHeight得到顯示rect大小
  • 根據(jù)不同OverFlow類型計算Label的寬度和高度。
    2.1.2 OverFlow的處理方式
  • ShrinkContent:縮放內(nèi)容;不斷遞減mPrintedSize調(diào)用NGUIText.WrapText(mText, out mProcessedText, true);判斷目前字體大小是否能顯示全部文字,直至滿足條件或者字體大小不能再減小。
  • ClampContent:裁切內(nèi)容;調(diào)用調(diào)用NGUIText.WrapText(mText, out mProcessedText, true);得到最終顯示的文字。
  • ResizeFreely:自適應寬高;調(diào)用調(diào)用NGUIText.WrapText(mText, out mProcessedText, true);得到最終顯示的文字。并調(diào)用NGUIText.CalculatePrintedSize(mProcessedText);得到顯示的大小,重新計算widget的寬高
  • ResizeHeight:自適應高度;類似ResizeFreely,只是重新計算widget的高度。

2.1.3 代碼

    void ProcessText (bool legacyMode, bool full)
    {
        if (!isValid) return;

        mChanged = true;
        shouldBeProcessed = false;

        float regionX = mDrawRegion.z - mDrawRegion.x;
        float regionY = mDrawRegion.w - mDrawRegion.y;

        NGUIText.rectWidth    = legacyMode ? (mMaxLineWidth  != 0 ? mMaxLineWidth  : 1000000) : width;
        NGUIText.rectHeight   = legacyMode ? (mMaxLineHeight != 0 ? mMaxLineHeight : 1000000) : height;
        NGUIText.regionWidth  = (regionX != 1f) ? Mathf.RoundToInt(NGUIText.rectWidth  * regionX) : NGUIText.rectWidth;
        NGUIText.regionHeight = (regionY != 1f) ? Mathf.RoundToInt(NGUIText.rectHeight * regionY) : NGUIText.rectHeight;

        mPrintedSize = Mathf.Abs(legacyMode ? Mathf.RoundToInt(cachedTransform.localScale.x) : defaultFontSize);
        mScale = 1f;

        if (NGUIText.regionWidth < 1 || NGUIText.regionHeight < 0)
        {
            mProcessedText = "";
            return;
        }

#if DYNAMIC_FONT
        bool isDynamic = (trueTypeFont != null);

        if (isDynamic && keepCrisp)
        {
            UIRoot rt = root;
            if (rt != null) mDensity = (rt != null) ? rt.pixelSizeAdjustment : 1f;
        }
        else mDensity = 1f;
#endif
        if (full) UpdateNGUIText();

        if (mOverflow == Overflow.ResizeFreely)
        {
            NGUIText.rectWidth = 1000000;
            NGUIText.regionWidth = 1000000;
        }

        if (mOverflow == Overflow.ResizeFreely || mOverflow == Overflow.ResizeHeight)
        {
            NGUIText.rectHeight = 1000000;
            NGUIText.regionHeight = 1000000;
        }

        if (mPrintedSize > 0)
        {
#if DYNAMIC_FONT
            bool adjustSize = keepCrisp;
#endif
            for (int ps = mPrintedSize; ps > 0; --ps)
            {
#if DYNAMIC_FONT
                // Adjust either the size, or the scale
                if (adjustSize)
                {
                    mPrintedSize = ps;
                    NGUIText.fontSize = mPrintedSize;
                }
                else
#endif
                {
                    mScale = (float)ps / mPrintedSize;
#if DYNAMIC_FONT
                    NGUIText.fontScale = isDynamic ? mScale : ((float)mFontSize / mFont.defaultSize) * mScale;
#else
                    NGUIText.fontScale = ((float)mFontSize / mFont.defaultSize) * mScale;
#endif
                }

                NGUIText.Update(false);

                // Wrap the text
                bool fits = NGUIText.WrapText(mText, out mProcessedText, true);

                if (mOverflow == Overflow.ShrinkContent && !fits)
                {
                    if (--ps > 1) continue;
                    else break;
                }
                else if (mOverflow == Overflow.ResizeFreely)
                {
                    mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);

                    mWidth = Mathf.Max(minWidth, Mathf.RoundToInt(mCalculatedSize.x));
                    if (regionX != 1f) mWidth = Mathf.RoundToInt(mWidth / regionX);
                    mHeight = Mathf.Max(minHeight, Mathf.RoundToInt(mCalculatedSize.y));
                    if (regionY != 1f) mHeight = Mathf.RoundToInt(mHeight / regionY);

                    if ((mWidth & 1) == 1) ++mWidth;
                    if ((mHeight & 1) == 1) ++mHeight;
                }
                else if (mOverflow == Overflow.ResizeHeight)
                {
                    mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);
                    mHeight = Mathf.Max(minHeight, Mathf.RoundToInt(mCalculatedSize.y));
                    if (regionY != 1f) mHeight = Mathf.RoundToInt(mHeight / regionY);
                    if ((mHeight & 1) == 1) ++mHeight;
                }
                else
                {
                    mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);
                }

                // Upgrade to the new system
                if (legacyMode)
                {
                    width = Mathf.RoundToInt(mCalculatedSize.x);
                    height = Mathf.RoundToInt(mCalculatedSize.y);
                    cachedTransform.localScale = Vector3.one;
                }
                break;
            }
        }
        else
        {
            cachedTransform.localScale = Vector3.one;
            mProcessedText = "";
            mScale = 1f;
        }
        
        if (full)
        {
            NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
            NGUIText.dynamicFont = null;
#endif
        }
    }

2.2 OnFill
2.2.1 流程

  • UpdateNGUIText:更新設置當前UILabel的屬性給NGUIText
  • NGUIText.Print(text, verts, uvs, cols):根據(jù)顯示的文本填入幾何數(shù)據(jù)到緩存中
  • ApplyOffset:根據(jù)Pivot類型調(diào)整頂點位置
  • 對于Effect非None情形,以下是3個類型的原理
    • Effect.Shadow:陰影;調(diào)用ApplyShadow增加陰影處理,ApplyShadow的作用是將之前填入的當前UILabel最終顯示的文本頂點、UV、Color數(shù)據(jù)重新填入一次,其中頂點做根據(jù)UILabel的mEffectDistance的屬性做相應的偏差處理,Effect.Shadow是偏右下,因此呈現(xiàn)出陰影效果。
    • Effect.Outline:描邊;調(diào)用ApplyShadow增加4個方向的陰影處理,分別是右下,左上,右上,左下,則相當于描邊作用
    • Effect.Outline8:8個方向的描邊,類似于Effect.Outline,方向相對于前置增加了正右,正上,正左,正下。

2.2.2 代碼

public override void OnFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
    {
        if (!isValid) return;

        int offset = verts.size;
        Color col = color;
        col.a = finalAlpha;
        
        if (mFont != null && mFont.premultipliedAlphaShader) col = NGUITools.ApplyPMA(col);

        if (QualitySettings.activeColorSpace == ColorSpace.Linear)
        {
            col.r = Mathf.GammaToLinearSpace(col.r);
            col.g = Mathf.GammaToLinearSpace(col.g);
            col.b = Mathf.GammaToLinearSpace(col.b);
        }

        string text = processedText;
        int start = verts.size;

        UpdateNGUIText();

        NGUIText.tint = col;
        NGUIText.Print(text, verts, uvs, cols);
        NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
        NGUIText.dynamicFont = null;
#endif
        // Center the content within the label vertically
        Vector2 pos = ApplyOffset(verts, start);

        // Effects don't work with packed fonts
        if (mFont != null && mFont.packedFontShader) return;

        // Apply an effect if one was requested
        if (effectStyle != Effect.None)
        {
            int end = verts.size;
            pos.x = mEffectDistance.x;
            pos.y = mEffectDistance.y;

            ApplyShadow(verts, uvs, cols, offset, end, pos.x, -pos.y);

            if ((effectStyle == Effect.Outline) || (effectStyle == Effect.Outline8))
            {
                offset = end;
                end = verts.size;

                ApplyShadow(verts, uvs, cols, offset, end, -pos.x, pos.y);

                offset = end;
                end = verts.size;

                ApplyShadow(verts, uvs, cols, offset, end, pos.x, pos.y);

                offset = end;
                end = verts.size;

                ApplyShadow(verts, uvs, cols, offset, end, -pos.x, -pos.y);

                if (effectStyle == Effect.Outline8)
                {
                    offset = end;
                    end = verts.size;

                    ApplyShadow(verts, uvs, cols, offset, end, -pos.x, 0);

                    offset = end;
                    end = verts.size;

                    ApplyShadow(verts, uvs, cols, offset, end, pos.x, 0);

                    offset = end;
                    end = verts.size;

                    ApplyShadow(verts, uvs, cols, offset, end, 0, pos.y);

                    offset = end;
                    end = verts.size;

                    ApplyShadow(verts, uvs, cols, offset, end, 0, -pos.y);
                }
            }
        }

        if (onPostFill != null)
            onPostFill(this, offset, verts, uvs, cols);
    }
  1. NGUIText
    3.1 NGUIText核心方法
    NGUIText.Print(text, verts, uvs, cols):(普通字符的處理、圖文字符的處理),頂點、UV和Color數(shù)據(jù)分別按照左下、左上、右上、右下順序填入緩存中。
    3.1.1 流程
  • Prepare:當使用動態(tài)字體時,調(diào)用Font.RequestCharactersInTexture刷新所需字符的紋理
  • 遍歷text的每個字符
  • 處理換行符 不處理非法字符
  • ParseSymbol 解析BBCode:該函數(shù)帶有多個ref值,分別為subscriptMode上下標,bold加粗,italic斜體, underline下劃線, strikethrough中劃線, ignoreColor忽略widget的顏色,即使用富文本的顏色。
  • GetSymbol:獲取對應的圖文字符信息,沒有匹配則返回null
    • 處理圖文字符

      • 計算符號圖文的顯示范圍
      • 如果寬度容納不下 換行
      • 添加頂點、UV、Color信息到緩存
      • 其中SymbolStyle等于Colored的時候,圖案的顏色使用當前字體顏色,其中情況下只使用當前的alpha值,rgb通道均為255。
    • 處理普通字符

      • GetGlyph(ch, prev);獲取字形信息,根據(jù)使用的是bitmapFont還是dynamicFont計算得到字形數(shù)據(jù)GlyphInfo,里面包含字形的頂點坐標,uv坐標和顏色channel通道設置。

        • GlyphInfo數(shù)據(jù)結(jié)構(gòu)
        public class GlyphInfo
          {
              public Vector2 v0;
              public Vector2 v1;
              public Vector2 u0;
              public Vector2 u1;
              public Vector2 u2;
              public Vector2 u3;
              public float advance = 0f;
              public int channel = 0;
        }       
        
    • 有上下標

      • glyph的兩個頂點glyph.v0和glyph.v1乘以一個比例值sizeShrinkage都縮小,縮小頂點范圍
      • 根據(jù)上標還是下標對頂點進行上下偏移Y坐標
    • 如果寬度不夠 換行處理

    • 若字符為空格:若BBCode對應是下劃線,則替換為“下劃線”;若對應中劃線,都不對應,則continue

    • 處理紋理坐標:添加紋理坐標到緩存中

    • 根據(jù)GlyphInfo的channel值-》處理頂點顏色,填入Color信息

      • 當前channel為0或者為15的時候,對于漸變情況做下處理,算法是:計算出字體頂點y軸最高點和最低點的在整體字體顯示的比例,通過漸變底部和頂部的顏色插值得到對應頂部和底部兩個顏色值。
    • 根據(jù)粗體斜體處理頂點坐標

      • 粗體 相當于每個字符渲染4次,對頂點坐標進行一定的偏移
      • 斜體 相當于每個字符頂點有一定偏移
    • 處理下劃線或中劃線

      • 獲取對應GlyphInfo信息
      • 類似上述進行處理
  • 處理alignment為居中或右側(cè)的情況:默認按照從左往右,即Alignment.Left方式處理
    • Alignment.Right:計算顯示寬度與文本寬度的差值,如果差值padding大于0,將頂點坐標的x坐標向右偏移padding
    • Alignment.Center:計算顯示寬度與文本寬度的差值的一半padding,如果差值padding大于0,將頂點坐標的x坐標向右偏移padding
    • Alignment.Justified:
      • 文本寬度需要大于顯示寬度的65%;
      • 計算顯示寬度與文本寬度的差值的一半padding
      • 將字符平均顯示在顯示區(qū)域上

3.1.2 代碼

static public void Print (string text, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
    {
        if (string.IsNullOrEmpty(text)) return;

        int indexOffset = verts.size;
        Prepare(text);

        // Start with the white tint
        mColors.Add(Color.white);
        mAlpha = 1f;

        int ch = 0, prev = 0;
        float x = 0f, y = 0f, maxX = 0f;
        float sizeF = finalSize;

        Color gb = tint * gradientBottom;
        Color gt = tint * gradientTop;
        Color32 uc = tint;
        int textLength = text.Length;

        Rect uvRect = new Rect();
        float invX = 0f, invY = 0f;
        float sizePD = sizeF * pixelDensity;

        // Advanced symbol support contributed by Rudy Pangestu.
        bool subscript = false;
        int subscriptMode = 0;  // 0 = normal, 1 = subscript, 2 = superscript
        bool bold = false;
        bool italic = false;
        bool underline = false;
        bool strikethrough = false;
        bool ignoreColor = false;
        const float sizeShrinkage = 0.75f;

        float v0x;
        float v1x;
        float v1y;
        float v0y;
        float prevX = 0;

        if (bitmapFont != null)
        {
            uvRect = bitmapFont.uvRect;
            invX = uvRect.width / bitmapFont.texWidth;
            invY = uvRect.height / bitmapFont.texHeight;
        }

        for (int i = 0; i < textLength; ++i)
        {
            ch = text[i];

            prevX = x;

            // New line character -- skip to the next line
            if (ch == '\n')
            {
                if (x > maxX) maxX = x;

                if (alignment != Alignment.Left)
                {
                    Align(verts, indexOffset, x - finalSpacingX);
                    indexOffset = verts.size;
                }

                x = 0;
                y += finalLineHeight;
                prev = 0;
                continue;
            }

            // Invalid character -- skip it
            if (ch < ' ')
            {
                prev = ch;
                continue;
            }

            // Color changing symbol
            if (encoding && ParseSymbol(text, ref i, mColors, premultiply, ref subscriptMode, ref bold,
                ref italic, ref underline, ref strikethrough, ref ignoreColor))
            {
                Color fc;

                if (ignoreColor)
                {
                    fc = mColors[mColors.size - 1];
                    fc.a *= mAlpha * tint.a;
                }
                else
                {
                    fc = tint * mColors[mColors.size - 1];
                    fc.a *= mAlpha;
                }
                uc = fc;

                for (int b = 0, bmax = mColors.size - 2; b < bmax; ++b)
                    fc.a *= mColors[b].a;

                if (gradient)
                {
                    gb = gradientBottom * fc;
                    gt = gradientTop * fc;
                }
                --i;
                continue;
            }

            // See if there is a symbol matching this text
            BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;

            if (symbol != null)
            {
                v0x = x + symbol.offsetX * fontScale;
                v1x = v0x + symbol.width * fontScale;
                v1y = -(y + symbol.offsetY * fontScale);
                v0y = v1y - symbol.height * fontScale;

                // Doesn't fit? Move down to the next line
                if (Mathf.RoundToInt(x + symbol.advance * fontScale) > regionWidth)
                {
                    if (x == 0f) return;

                    if (alignment != Alignment.Left && indexOffset < verts.size)
                    {
                        Align(verts, indexOffset, x - finalSpacingX);
                        indexOffset = verts.size;
                    }

                    v0x -= x;
                    v1x -= x;
                    v0y -= finalLineHeight;
                    v1y -= finalLineHeight;

                    x = 0;
                    y += finalLineHeight;
                    prevX = 0;
                }

                verts.Add(new Vector3(v0x, v0y));
                verts.Add(new Vector3(v0x, v1y));
                verts.Add(new Vector3(v1x, v1y));
                verts.Add(new Vector3(v1x, v0y));

                x += finalSpacingX + symbol.advance * fontScale;
                i += symbol.length - 1;
                prev = 0;

                if (uvs != null)
                {
                    Rect uv = symbol.uvRect;

                    float u0x = uv.xMin;
                    float u0y = uv.yMin;
                    float u1x = uv.xMax;
                    float u1y = uv.yMax;

                    uvs.Add(new Vector2(u0x, u0y));
                    uvs.Add(new Vector2(u0x, u1y));
                    uvs.Add(new Vector2(u1x, u1y));
                    uvs.Add(new Vector2(u1x, u0y));
                }

                if (cols != null)
                {
                    if (symbolStyle == SymbolStyle.Colored)
                    {
                        for (int b = 0; b < 4; ++b) cols.Add(uc);
                    }
                    else
                    {
                        Color32 col = Color.white;
                        col.a = uc.a;
                        for (int b = 0; b < 4; ++b) cols.Add(col);
                    }
                }
            }
            else // No symbol present
            {
                GlyphInfo glyph = GetGlyph(ch, prev);
                if (glyph == null) continue;
                prev = ch;

                if (subscriptMode != 0)
                {
                    glyph.v0.x *= sizeShrinkage;
                    glyph.v0.y *= sizeShrinkage;
                    glyph.v1.x *= sizeShrinkage;
                    glyph.v1.y *= sizeShrinkage;

                    if (subscriptMode == 1)
                    {
                        glyph.v0.y -= fontScale * fontSize * 0.4f;
                        glyph.v1.y -= fontScale * fontSize * 0.4f;
                    }
                    else
                    {
                        glyph.v0.y += fontScale * fontSize * 0.05f;
                        glyph.v1.y += fontScale * fontSize * 0.05f;
                    }
                }

                v0x = glyph.v0.x + x;
                v0y = glyph.v0.y - y;
                v1x = glyph.v1.x + x;
                v1y = glyph.v1.y - y;

                float w = glyph.advance;
                if (finalSpacingX < 0f) w += finalSpacingX;

                // Doesn't fit? Move down to the next line
                if (Mathf.RoundToInt(x + w) > regionWidth)
                {
                    if (x == 0f) return;

                    if (alignment != Alignment.Left && indexOffset < verts.size)
                    {
                        Align(verts, indexOffset, x - finalSpacingX);
                        indexOffset = verts.size;
                    }

                    v0x -= x;
                    v1x -= x;
                    v0y -= finalLineHeight;
                    v1y -= finalLineHeight;

                    x = 0;
                    y += finalLineHeight;
                    prevX = 0;
                }

                if (IsSpace(ch))
                {
                    if (underline)
                    {
                        ch = '_';
                    }
                    else if (strikethrough)
                    {
                        ch = '-';
                    }
                }

                // Advance the position
                x += (subscriptMode == 0) ? finalSpacingX + glyph.advance :
                    (finalSpacingX + glyph.advance) * sizeShrinkage;

                // No need to continue if this is a space character
                if (IsSpace(ch)) continue;

                // Texture coordinates
                if (uvs != null)
                {
                    if (bitmapFont != null)
                    {
                        glyph.u0.x = uvRect.xMin + invX * glyph.u0.x;
                        glyph.u2.x = uvRect.xMin + invX * glyph.u2.x;
                        glyph.u0.y = uvRect.yMax - invY * glyph.u0.y;
                        glyph.u2.y = uvRect.yMax - invY * glyph.u2.y;

                        glyph.u1.x = glyph.u0.x;
                        glyph.u1.y = glyph.u2.y;

                        glyph.u3.x = glyph.u2.x;
                        glyph.u3.y = glyph.u0.y;
                    }

                    for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
                    {
                        uvs.Add(glyph.u0);
                        uvs.Add(glyph.u1);
                        uvs.Add(glyph.u2);
                        uvs.Add(glyph.u3);
                    }
                }

                // Vertex colors
                if (cols != null)
                {
                    if (glyph.channel == 0 || glyph.channel == 15)
                    {
                        if (gradient)
                        {
                            float min = sizePD + glyph.v0.y / fontScale;
                            float max = sizePD + glyph.v1.y / fontScale;

                            min /= sizePD;
                            max /= sizePD;

                            s_c0 = Color.Lerp(gb, gt, min);
                            s_c1 = Color.Lerp(gb, gt, max);

                            for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
                            {
                                cols.Add(s_c0);
                                cols.Add(s_c1);
                                cols.Add(s_c1);
                                cols.Add(s_c0);
                            }
                        }
                        else
                        {
                            for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
                                cols.Add(uc);
                        }
                    }
                    else
                    {
                        // Packed fonts come as alpha masks in each of the RGBA channels.
                        // In order to use it we need to use a special shader.
                        //
                        // Limitations:
                        // - Effects (drop shadow, outline) will not work.
                        // - Should not be a part of the atlas (eastern fonts rarely are anyway).
                        // - Lower color precision

                        Color col = uc;

                        col *= 0.49f;

                        switch (glyph.channel)
                        {
                            case 1: col.b += 0.51f; break;
                            case 2: col.g += 0.51f; break;
                            case 4: col.r += 0.51f; break;
                            case 8: col.a += 0.51f; break;
                        }

                        Color32 c = col;
                        for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
                            cols.Add(c);
                    }
                }

                // Bold and italic contributed by Rudy Pangestu.
                if (!bold)
                {
                    if (!italic)
                    {
                        verts.Add(new Vector3(v0x, v0y));
                        verts.Add(new Vector3(v0x, v1y));
                        verts.Add(new Vector3(v1x, v1y));
                        verts.Add(new Vector3(v1x, v0y));
                    }
                    else // Italic
                    {
                        float slant = fontSize * 0.1f * ((v1y - v0y) / fontSize);
                        verts.Add(new Vector3(v0x - slant, v0y));
                        verts.Add(new Vector3(v0x + slant, v1y));
                        verts.Add(new Vector3(v1x + slant, v1y));
                        verts.Add(new Vector3(v1x - slant, v0y));
                    }
                }
                else // Bold
                {
                    for (int j = 0; j < 4; ++j)
                    {
                        float a = mBoldOffset[j * 2];
                        float b = mBoldOffset[j * 2 + 1];

                        float slant = (italic ? fontSize * 0.1f * ((v1y - v0y) / fontSize) : 0f);
                        verts.Add(new Vector3(v0x + a - slant, v0y + b));
                        verts.Add(new Vector3(v0x + a + slant, v1y + b));
                        verts.Add(new Vector3(v1x + a + slant, v1y + b));
                        verts.Add(new Vector3(v1x + a - slant, v0y + b));
                    }
                }

                // Underline and strike-through contributed by Rudy Pangestu.
                if (underline || strikethrough)
                {
                    GlyphInfo dash = GetGlyph(strikethrough ? '-' : '_', prev);
                    if (dash == null) continue;

                    if (uvs != null)
                    {
                        if (bitmapFont != null)
                        {
                            dash.u0.x = uvRect.xMin + invX * dash.u0.x;
                            dash.u2.x = uvRect.xMin + invX * dash.u2.x;
                            dash.u0.y = uvRect.yMax - invY * dash.u0.y;
                            dash.u2.y = uvRect.yMax - invY * dash.u2.y;
                        }

                        float cx = (dash.u0.x + dash.u2.x) * 0.5f;

                        for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
                        {
                            uvs.Add(new Vector2(cx, dash.u0.y));
                            uvs.Add(new Vector2(cx, dash.u2.y));
                            uvs.Add(new Vector2(cx, dash.u2.y));
                            uvs.Add(new Vector2(cx, dash.u0.y));
                        }
                    }

                    if (subscript && strikethrough)
                    {
                        v0y = (-y + dash.v0.y) * sizeShrinkage;
                        v1y = (-y + dash.v1.y) * sizeShrinkage;
                    }
                    else
                    {
                        v0y = (-y + dash.v0.y);
                        v1y = (-y + dash.v1.y);
                    }

                    if (bold)
                    {
                        for (int j = 0; j < 4; ++j)
                        {
                            float a = mBoldOffset[j * 2];
                            float b = mBoldOffset[j * 2 + 1];

                            verts.Add(new Vector3(prevX + a, v0y + b));
                            verts.Add(new Vector3(prevX + a, v1y + b));
                            verts.Add(new Vector3(x + a, v1y + b));
                            verts.Add(new Vector3(x + a, v0y + b));
                        }
                    }
                    else
                    {
                        verts.Add(new Vector3(prevX, v0y));
                        verts.Add(new Vector3(prevX, v1y));
                        verts.Add(new Vector3(x, v1y));
                        verts.Add(new Vector3(x, v0y));
                    }

                    if (gradient)
                    {
                        float min = sizePD + dash.v0.y / fontScale;
                        float max = sizePD + dash.v1.y / fontScale;

                        min /= sizePD;
                        max /= sizePD;

                        s_c0 = Color.Lerp(gb, gt, min);
                        s_c1 = Color.Lerp(gb, gt, max);

                        for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
                        {
                            cols.Add(s_c0);
                            cols.Add(s_c1);
                            cols.Add(s_c1);
                            cols.Add(s_c0);
                        }
                    }
                    else
                    {
                        for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
                            cols.Add(uc);
                    }
                }
            }
        }

        if (alignment != Alignment.Left && indexOffset < verts.size)
        {
            Align(verts, indexOffset, x - finalSpacingX);
            indexOffset = verts.size;
        }
        mColors.Clear();
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容