NGUI中动态字体图文混排

用到的场景:聊天系统。

NGUI版本:3.5.6。


已支持的功能,静态(位图)字体的图文混排。


需要实现的:动态字体的图文混排。


实现思路:用两个Label,一个只负责渲染文字,一个负责渲染图片。用isSymbol区分是渲染图片还是渲染文字。渲染文字的跳过符号的顶点和uv的添加,渲染图片的跳过正常字符的顶点和uv添加。


修改NGUIText.cs的代码如下:

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);

		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;
		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;
        }
        else
        {
            if (symbolFont != null && useWgSymbol)
            {
                uvRect = symbolFont.uvRect;
                invX = uvRect.width / symbolFont.texWidth;
                invY = uvRect.height / symbolFont.texHeight;
            }
        }



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

			prevX = x;

			// New line character -- skip to the next line
			if (ch == '
')
			{
				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))
			{
				Color fc = tint * mColors[mColors.size - 1];
				uc = fc;

				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) > rectWidth)
				{
					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 (isSymbol || !useWgSymbol)
                {
                    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;
                    if (isSymbol || !useWgSymbol)
                    {
                        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)
                    {
                        if (isSymbol || !useWgSymbol)
                        {
                            for (int b = 0; b < 4; ++b) cols.Add(uc);
                        }
					}
					else
					{
						Color32 col = Color.white;
                        col.a = uc.a;
                        if (isSymbol || !useWgSymbol)
                        {
                            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;
					}
				}

				float y0 = glyph.v0.y;
				float y1 = glyph.v1.y;

				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) > rectWidth)
				{
					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 (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 (ch == ' ') continue;

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

                    if (!isSymbol || !useWgSymbol)
                    {
                        for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
                        {
                            if (glyph.rotatedUVs)
                            {
                                uvs.Add(glyph.u0);
                                uvs.Add(new Vector2(glyph.u1.x, glyph.u0.y));
                                uvs.Add(glyph.u1);
                                uvs.Add(new Vector2(glyph.u0.x, glyph.u1.y));
                            }
                            else
                            {
                                uvs.Add(glyph.u0);
                                uvs.Add(new Vector2(glyph.u0.x, glyph.u1.y));
                                uvs.Add(glyph.u1);
                                uvs.Add(new Vector2(glyph.u1.x, glyph.u0.y));
                            }
                        }
                    }
				}

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

							min /= sizePD;
							max /= sizePD;

							s_c0 = Color.Lerp(gb, gt, min);
							s_c1 = Color.Lerp(gb, gt, max);
                            if (!isSymbol || !useWgSymbol)
                            {
                                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
                        {
                            if (!isSymbol || !useWgSymbol)
                            {
                                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;
						}
                        if (!isSymbol || !useWgSymbol)
                        {
                            Color32 c = col;
                            for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
                                cols.Add(c);
                        }
					}
				}
                if (!isSymbol || !useWgSymbol)
                {
                    // 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 = a + (italic ? fontSize * 0.1f * ((v1y - v0y) / fontSize) : 0f);
                            verts.Add(new Vector3(v0x - slant, v0y + b));
                            verts.Add(new Vector3(v0x + slant, v1y + b));
                            verts.Add(new Vector3(v1x + slant, v1y + b));
                            verts.Add(new Vector3(v1x - slant, v0y + b));
                        }
                    }
                }

				// Underline and strike-through contributed by Rudy Pangestu.
				if (underline || strikethrough)
				{
					GlyphInfo dash = GetGlyph(strikethrough ? '-' : '_', prev);
					if (dash == null) continue;
                    if (!isSymbol || !useWgSymbol)
                    {
                        if (uvs != null)
                        {
                            if (bitmapFont != null)
                            {
                                dash.u0.x = uvRect.xMin + invX * dash.u0.x;
                                dash.u1.x = uvRect.xMin + invX * dash.u1.x;
                                dash.u0.y = uvRect.yMax - invY * dash.u0.y;
                                dash.u1.y = uvRect.yMax - invY * dash.u1.y;
                            }

                            float cx = (dash.u0.x + dash.u1.x) * 0.5f;
                            float cy = (dash.u0.y + dash.u1.y) * 0.5f;

                            uvs.Add(new Vector2(cx, cy));
                            uvs.Add(new Vector2(cx, cy));
                            uvs.Add(new Vector2(cx, cy));
                            uvs.Add(new Vector2(cx, cy));
                        }
                    }

					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 (!isSymbol || !useWgSymbol)
                    {
                        verts.Add(new Vector3(prevX, v0y));
                        verts.Add(new Vector3(prevX, v1y));
                        verts.Add(new Vector3(x, v1y));
                        verts.Add(new Vector3(x, v0y));
                    }

					Color tint2 = uc;

					if (strikethrough)
					{
						tint2.r *= 0.5f;
						tint2.g *= 0.5f;
						tint2.b *= 0.5f;
					}
					tint2.a *= 0.75f;
					Color32 uc2 = tint2;
                    if (!isSymbol || !useWgSymbol)
                    {
                        cols.Add(uc2);
                        cols.Add(uc);
                        cols.Add(uc);
                        cols.Add(uc2);
                    }
				}
			}
		}

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


原文地址:https://www.cnblogs.com/wugang/p/14232324.html