// OnRender protected override void OnRender() { if ((Size.Y <= 0.0f) || (!ColorFinal.HasAlpha() && ((RenderState.BlendState == E_BlendState.AlphaBlend) || (RenderState.BlendState == E_BlendState.NonPremultiplied)))) { return; } if (FontStyle == null) { return; } if ((_String == null) && (StringUI == null)) { return; } using (new AutoTransform(this)) { if (FontEffects.Count > 0) { _UI.Font.DoFontEffectsNext(FontEffects); } if (Cache && CacheValid) { FontStyle.Render(SpriteListCache, RenderSizeY); } else { Vector2 stringSize = new Vector2(); if (StringUI != null) { stringSize = _UI.Font.Draw(StringUI, FontStyle, RenderPass, Layer, ref Position, RenderSizeY, Align, ref ColorFinal, RenderSizeX); } else { stringSize = _UI.Font.Draw(_String, FontStyle, RenderPass, Layer, ref Position, RenderSizeY, Align, ref ColorFinal, RenderSizeX); } // slag lag - are we bothered? We'll see ... Size.X = stringSize.X; Size.Y = stringSize.Y; if (Cache) { CacheValid = true; _UI.Font.SpriteList.CopyTo(SpriteListCache); } } for (int i = 0; i < FontEffects.Count; ++i) { FontEffects[i].Update(FrameTime); // meh - but need to update here to make sure the letter count is correct } } // auto transform }
private unsafe Vector2 Process( ref StringProxy text, FontStyle fontStyle, float height, float wrapSizeX, E_FontProcessType processType ) { if ( text.Length == 0 ) return new Vector2(); SpriteList.Reset(); SpriteList.RenderPass = RenderPass; SpriteList.Layer = Layer; FontData fontData = fontStyle.Font.FontData; float startX = Position.X; float x = startX; float y = Position.Y; float scale = height / fontData.FontHeight; float charHeight = fontData.LineHeight * scale; float lineHeight = charHeight * fontStyle.HeightPercentage; float multY = _UI.Sprite.IsRenderPass3D( RenderPass ) ? -1.0f : 1.0f; int numLines = 1; fixed ( char* pString = text.String ) fixed ( FontSprite* pSpriteStart = &SpriteList.Sprites[ 0 ] ) fixed ( FontDataChar* pCharDataStart = &fontData.Characters[ 0 ] ) { char* s = ( pString != null ) ? pString : text.StringUI; for ( int i = 0; i < text.Length; ++i ) { char c = s[ i ]; if ( c == '\n' ) { ++numLines; y += ( lineHeight * multY ); x = startX; ( pSpriteStart + SpriteList.RenderEnd - 1 )->BreakType = E_LetterBreakType.Line; continue; } int characterIndex = fontData.GetCharacterIndex( c ); FontDataChar* pCharData = ( pCharDataStart + characterIndex ); if ( c == ' ' ) { // mark as potential wrap point unless we've already wrapped here FontSprite* pSpriteEnd = ( pSpriteStart + SpriteList.RenderEnd - 1 ); if ( ( SpriteList.RenderEnd != 0 ) && ( pSpriteEnd->BreakType != E_LetterBreakType.Line ) ) pSpriteEnd->BreakType = E_LetterBreakType.Wrap; // advance x += ( pCharData->AdvanceX * scale ); } else { // check for icons if ( ( ( i + 5 ) <= text.Length ) && ( c == '[' ) && ( s[ i + 1 ] == '[' ) ) { int j = i + 2; StringUI.Clear(); while ( ( j < text.Length ) && ( s[ j ] != ']' ) ) StringUI.Add( s[ j++ ] ); if ( ( ( j + 1 ) < text.Length ) && ( s[ j + 1 ] == ']' ) ) { // valid format int fontIconIndex = _UI.Store_FontIcon.Get( StringUI ); if ( fontIconIndex != -1 ) { // add to list FontIcon fontIcon = _UI.Store_FontIcon.Get( fontIconIndex ); FontSprite* pSprite = ( pSpriteStart + SpriteList.RenderEnd++ ); pSprite->Character = (char)0xffff; pSprite->Position.X = x; pSprite->Position.Y = y - ( ( ( charHeight * fontIcon.Scale ) - charHeight ) * 0.5f ) * multY; pSprite->Position.Z = Position.Z; float heightScaled = charHeight * fontIcon.Scale; pSprite->Size.X = heightScaled * fontIcon.AspectRatio; pSprite->Size.Y = heightScaled; fixed ( SpriteColor* pColor = &fontIcon.Color ) { pSprite->Colors.CopyA( ref Colors ); pSprite->Colors.MultA( pColor->A ); pSprite->Colors.R( pColor->R ); pSprite->Colors.G( pColor->G ); pSprite->Colors.B( pColor->B ); if ( Colors.PreMultipliedAlpha ) pSprite->Colors.ToPremultiplied(); } // set base texture pSprite->Texture.TextureIndex = fontIcon.Texture.TextureIndex; pSprite->Texture.PUV = fontIcon.Texture.PUV; pSprite->Texture.SUV = fontIcon.Texture.SUV; pSprite->BreakType = E_LetterBreakType.None; pSprite->IconIndex = fontIconIndex; // advance x += pSprite->Size.X; // advance string past the icon characters i += ( 4 + StringUI.Length - 1 ); } } } else { // add to list FontSprite* pSprite = ( pSpriteStart + SpriteList.RenderEnd++ ); pSprite->Character = c; pSprite->Position.X = x + ( pCharData->OffsetX * scale ); pSprite->Position.Y = y + ( pCharData->OffsetY * scale ) * multY; pSprite->Position.Z = Position.Z; pSprite->Size.X = pCharData->Width * scale; pSprite->Size.Y = pCharData->Height * scale; pSprite->Colors = Colors; // set base texture pSprite->Texture.TextureIndex = fontStyle.Font.TextureIndex; pSprite->Texture.PUV.X = (float)pCharData->X / fontData.TextureWidth; pSprite->Texture.PUV.Y = (float)pCharData->Y / fontData.TextureHeight; pSprite->Texture.SUV.X = (float)pCharData->Width / fontData.TextureWidth; pSprite->Texture.SUV.Y = (float)pCharData->Height / fontData.TextureHeight; // second (optional) texture is set through the FontStyle when rendering pSprite->BreakType = E_LetterBreakType.None; pSprite->IconIndex = -1; // advance x += ( pCharData->AdvanceX * scale ); // kerning if ( i != ( text.Length - 1 ) ) x += ( fontData.GetKerningAmount( c, s[ i + 1 ] ) * scale ); } } // check for wrapping if ( ( c != ' ' ) && ( wrapSizeX != 0.0f ) && ( ( x - startX ) > wrapSizeX ) ) { int iWrap = SpriteList.RenderEnd - 1; FontSprite* pSpriteWrap = ( pSpriteStart + iWrap ); // search backwards until we find a place to wrap while ( ( iWrap >= 0 ) && ( pSpriteWrap->BreakType != E_LetterBreakType.Wrap ) && ( pSpriteWrap->BreakType != E_LetterBreakType.Line ) ) pSpriteWrap = ( pSpriteStart + --iWrap ); bool wrapOnLine = false; if ( iWrap != -1 ) { if ( pSpriteWrap->BreakType == E_LetterBreakType.Line ) wrapOnLine = true; // word is longer than the wrap size else pSpriteWrap->BreakType = E_LetterBreakType.Line; } else { if ( SpriteList.RenderEnd > 1 ) wrapOnLine = true; // long first word with leading spaces, wtf } ++pSpriteWrap; float diffX = pSpriteWrap->Position.X - startX; float offsetX = x - pSpriteWrap->Position.X; // shift the characters after the break point for ( int j = ( iWrap + 1 ); j < SpriteList.RenderEnd; ++j ) { pSpriteWrap->Position.X -= diffX; if ( !wrapOnLine ) pSpriteWrap->Position.Y += ( lineHeight * multY ); ++pSpriteWrap; } ++numLines; if ( !wrapOnLine ) y += ( lineHeight * multY ); x = startX + offsetX; } // tracking x += ( height * fontStyle.TrackingPercentage ); } ( pSpriteStart + SpriteList.RenderEnd - 1 )->BreakType = E_LetterBreakType.Line; float totalHeight = ( ( numLines - 1 ) * lineHeight ) + charHeight; if ( processType == E_FontProcessType.Draw ) { // align if ( ( Align != E_Align.TopLeft ) && ( Align != E_Align.None ) ) { Vector2 offset = _UI.Sprite.GetVertexOffsetAligned( 0, Align ); if ( ( Align == E_Align.MiddleLeft ) || ( Align == E_Align.BottomLeft ) ) { // only just need to offset y FontSprite* pSpriteToOffset = pSpriteStart; for ( int i = 0; i < SpriteList.RenderEnd; ++i, ++pSpriteToOffset ) pSpriteToOffset->Position.Y -= ( totalHeight * offset.Y ) * multY; } else { // need to offset both x and y int iLineStart = 0; FontSprite* pSpriteLineEnd = pSpriteStart; for ( int i = 0; i < SpriteList.RenderEnd; ++i, ++pSpriteLineEnd ) { if ( pSpriteLineEnd->BreakType != E_LetterBreakType.Line ) continue; float lineWidth = ( pSpriteLineEnd->Position.X + pSpriteLineEnd->Size.X ) - startX; // offset every sprite on this line FontSprite* pSpriteToOffset = ( pSpriteStart + iLineStart ); for ( int j = iLineStart; j <= i; ++j, ++pSpriteToOffset ) { pSpriteToOffset->Position.X -= ( lineWidth * offset.X ); pSpriteToOffset->Position.Y -= ( totalHeight * offset.Y ) * multY; } iLineStart = i + 1; } } } // pre-render if ( FontEffects != null ) { for ( int i = 0; i < FontEffects.Count; ++i ) FontEffects[ i ].Process( SpriteList ); FontEffects = null; } // actual render fontStyle.Render( SpriteList, height ); } return new Vector2( ( numLines == 1 ) ? ( x - startX ) : wrapSizeX, totalHeight ); } }
private unsafe Vector2 Process(ref StringProxy text, FontStyle fontStyle, float height, float wrapSizeX, E_FontProcessType processType) { if (text.Length == 0) { return(new Vector2()); } SpriteList.Reset(); SpriteList.RenderPass = RenderPass; SpriteList.Layer = Layer; FontData fontData = fontStyle.Font.FontData; float startX = Position.X; float x = startX; float y = Position.Y; float scale = height / fontData.FontHeight; float charHeight = fontData.LineHeight * scale; float lineHeight = charHeight * fontStyle.HeightPercentage; float multY = _UI.Sprite.IsRenderPass3D(RenderPass) ? -1.0f : 1.0f; int numLines = 1; fixed(char *pString = text.String) fixed(FontSprite * pSpriteStart = &SpriteList.Sprites[0]) fixed(FontDataChar * pCharDataStart = &fontData.Characters[0]) { char *s = (pString != null) ? pString : text.StringUI; for (int i = 0; i < text.Length; ++i) { char c = s[i]; if (c == '\n') { ++numLines; y += (lineHeight * multY); x = startX; (pSpriteStart + SpriteList.RenderEnd - 1)->BreakType = E_LetterBreakType.Line; continue; } int characterIndex = fontData.GetCharacterIndex(c); FontDataChar *pCharData = (pCharDataStart + characterIndex); if (c == ' ') { // mark as potential wrap point unless we've already wrapped here FontSprite *pSpriteEnd = (pSpriteStart + SpriteList.RenderEnd - 1); if ((SpriteList.RenderEnd != 0) && (pSpriteEnd->BreakType != E_LetterBreakType.Line)) { pSpriteEnd->BreakType = E_LetterBreakType.Wrap; } // advance x += (pCharData->AdvanceX * scale); } else { // check for icons if (((i + 5) <= text.Length) && (c == '[') && (s[i + 1] == '[')) { int j = i + 2; StringUI.Clear(); while ((j < text.Length) && (s[j] != ']')) { StringUI.Add(s[j++]); } if (((j + 1) < text.Length) && (s[j + 1] == ']')) { // valid format int fontIconIndex = _UI.Store_FontIcon.Get(StringUI); if (fontIconIndex != -1) { // add to list FontIcon fontIcon = _UI.Store_FontIcon.Get(fontIconIndex); FontSprite *pSprite = (pSpriteStart + SpriteList.RenderEnd++); pSprite->Character = (char)0xffff; pSprite->Position.X = x; pSprite->Position.Y = y - (((charHeight * fontIcon.Scale) - charHeight) * 0.5f) * multY; pSprite->Position.Z = Position.Z; float heightScaled = charHeight * fontIcon.Scale; pSprite->Size.X = heightScaled * fontIcon.AspectRatio; pSprite->Size.Y = heightScaled; fixed(SpriteColor *pColor = &fontIcon.Color) { pSprite->Colors.CopyA(ref Colors); pSprite->Colors.MultA(pColor->A); pSprite->Colors.R(pColor->R); pSprite->Colors.G(pColor->G); pSprite->Colors.B(pColor->B); if (Colors.PreMultipliedAlpha) { pSprite->Colors.ToPremultiplied(); } } // set base texture pSprite->Texture.TextureIndex = fontIcon.Texture.TextureIndex; pSprite->Texture.PUV = fontIcon.Texture.PUV; pSprite->Texture.SUV = fontIcon.Texture.SUV; pSprite->BreakType = E_LetterBreakType.None; pSprite->IconIndex = fontIconIndex; // advance x += pSprite->Size.X; // advance string past the icon characters i += (4 + StringUI.Length - 1); } } } else { // add to list FontSprite *pSprite = (pSpriteStart + SpriteList.RenderEnd++); pSprite->Character = c; pSprite->Position.X = x + (pCharData->OffsetX * scale); pSprite->Position.Y = y + (pCharData->OffsetY * scale) * multY; pSprite->Position.Z = Position.Z; pSprite->Size.X = pCharData->Width * scale; pSprite->Size.Y = pCharData->Height * scale; pSprite->Colors = Colors; // set base texture pSprite->Texture.TextureIndex = fontStyle.Font.TextureIndex; pSprite->Texture.PUV.X = (float)pCharData->X / fontData.TextureWidth; pSprite->Texture.PUV.Y = (float)pCharData->Y / fontData.TextureHeight; pSprite->Texture.SUV.X = (float)pCharData->Width / fontData.TextureWidth; pSprite->Texture.SUV.Y = (float)pCharData->Height / fontData.TextureHeight; // second (optional) texture is set through the FontStyle when rendering pSprite->BreakType = E_LetterBreakType.None; pSprite->IconIndex = -1; // advance x += (pCharData->AdvanceX * scale); // kerning if (i != (text.Length - 1)) { x += (fontData.GetKerningAmount(c, s[i + 1]) * scale); } } } // check for wrapping if ((c != ' ') && (wrapSizeX != 0.0f) && ((x - startX) > wrapSizeX)) { int iWrap = SpriteList.RenderEnd - 1; FontSprite *pSpriteWrap = (pSpriteStart + iWrap); // search backwards until we find a place to wrap while ((iWrap >= 0) && (pSpriteWrap->BreakType != E_LetterBreakType.Wrap) && (pSpriteWrap->BreakType != E_LetterBreakType.Line)) { pSpriteWrap = (pSpriteStart + --iWrap); } bool wrapOnLine = false; if (iWrap != -1) { if (pSpriteWrap->BreakType == E_LetterBreakType.Line) { wrapOnLine = true; // word is longer than the wrap size } else { pSpriteWrap->BreakType = E_LetterBreakType.Line; } } else { if (SpriteList.RenderEnd > 1) { wrapOnLine = true; // long first word with leading spaces, wtf } } ++pSpriteWrap; float diffX = pSpriteWrap->Position.X - startX; float offsetX = x - pSpriteWrap->Position.X; // shift the characters after the break point for (int j = (iWrap + 1); j < SpriteList.RenderEnd; ++j) { pSpriteWrap->Position.X -= diffX; if (!wrapOnLine) { pSpriteWrap->Position.Y += (lineHeight * multY); } ++pSpriteWrap; } ++numLines; if (!wrapOnLine) { y += (lineHeight * multY); } x = startX + offsetX; } // tracking x += (height * fontStyle.TrackingPercentage); } (pSpriteStart + SpriteList.RenderEnd - 1)->BreakType = E_LetterBreakType.Line; float totalHeight = ((numLines - 1) * lineHeight) + charHeight; if (processType == E_FontProcessType.Draw) { // align if ((Align != E_Align.TopLeft) && (Align != E_Align.None)) { Vector2 offset = _UI.Sprite.GetVertexOffsetAligned(0, Align); if ((Align == E_Align.MiddleLeft) || (Align == E_Align.BottomLeft)) { // only just need to offset y FontSprite *pSpriteToOffset = pSpriteStart; for (int i = 0; i < SpriteList.RenderEnd; ++i, ++pSpriteToOffset) { pSpriteToOffset->Position.Y -= (totalHeight * offset.Y) * multY; } } else { // need to offset both x and y int iLineStart = 0; FontSprite *pSpriteLineEnd = pSpriteStart; for (int i = 0; i < SpriteList.RenderEnd; ++i, ++pSpriteLineEnd) { if (pSpriteLineEnd->BreakType != E_LetterBreakType.Line) { continue; } float lineWidth = (pSpriteLineEnd->Position.X + pSpriteLineEnd->Size.X) - startX; // offset every sprite on this line FontSprite *pSpriteToOffset = (pSpriteStart + iLineStart); for (int j = iLineStart; j <= i; ++j, ++pSpriteToOffset) { pSpriteToOffset->Position.X -= (lineWidth * offset.X); pSpriteToOffset->Position.Y -= (totalHeight * offset.Y) * multY; } iLineStart = i + 1; } } } // pre-render if (FontEffects != null) { for (int i = 0; i < FontEffects.Count; ++i) { FontEffects[i].Process(SpriteList); } FontEffects = null; } // actual render fontStyle.Render(SpriteList, height); } return(new Vector2((numLines == 1) ? (x - startX) : wrapSizeX, totalHeight)); } }