//////////////////////////////////////////////////////////////// // // // Neoforce Controls // // // //////////////////////////////////////////////////////////////// // // // File: Renderer.cs // // // // Version: 0.7 // // // // Date: 11/09/2010 // // // // Author: Tom Shane // // // //////////////////////////////////////////////////////////////// // // // Copyright (c) by Tom Shane // // // //////////////////////////////////////////////////////////////// #region //// Using ///////////// //////////////////////////////////////////////////////////////////////////// using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; //////////////////////////////////////////////////////////////////////////// #endregion namespace TomShane.Neoforce.Controls { public enum BlendingMode { Default, None, } #region //// Classes /////////// //////////////////////////////////////////////////////////////////////////// public class DeviceStates { public readonly BlendState BlendState; public readonly RasterizerState RasterizerState; public readonly DepthStencilState DepthStencilState; public readonly SamplerState SamplerState; public DeviceStates() { BlendState = new BlendState(); BlendState.AlphaBlendFunction = BlendState.AlphaBlend.AlphaBlendFunction; BlendState.AlphaDestinationBlend = BlendState.AlphaBlend.AlphaDestinationBlend; BlendState.AlphaSourceBlend = BlendState.AlphaBlend.AlphaSourceBlend; BlendState.BlendFactor = BlendState.AlphaBlend.BlendFactor; BlendState.ColorBlendFunction = BlendState.AlphaBlend.ColorBlendFunction; BlendState.ColorDestinationBlend = BlendState.AlphaBlend.ColorDestinationBlend; BlendState.ColorSourceBlend = BlendState.AlphaBlend.ColorSourceBlend; BlendState.ColorWriteChannels = BlendState.AlphaBlend.ColorWriteChannels; BlendState.ColorWriteChannels1 = BlendState.AlphaBlend.ColorWriteChannels1; BlendState.ColorWriteChannels2 = BlendState.AlphaBlend.ColorWriteChannels2; BlendState.ColorWriteChannels3 = BlendState.AlphaBlend.ColorWriteChannels3; BlendState.MultiSampleMask = BlendState.AlphaBlend.MultiSampleMask; RasterizerState = new RasterizerState(); RasterizerState.CullMode = RasterizerState.CullNone.CullMode; RasterizerState.DepthBias = RasterizerState.CullNone.DepthBias; RasterizerState.FillMode = RasterizerState.CullNone.FillMode; RasterizerState.MultiSampleAntiAlias = RasterizerState.CullNone.MultiSampleAntiAlias; RasterizerState.ScissorTestEnable = RasterizerState.CullNone.ScissorTestEnable; RasterizerState.SlopeScaleDepthBias = RasterizerState.CullNone.SlopeScaleDepthBias; RasterizerState.ScissorTestEnable = true; SamplerState = new SamplerState(); SamplerState.AddressU = SamplerState.AnisotropicClamp.AddressU; SamplerState.AddressV = SamplerState.AnisotropicClamp.AddressV; SamplerState.AddressW = SamplerState.AnisotropicClamp.AddressW; SamplerState.Filter = SamplerState.AnisotropicClamp.Filter; SamplerState.MaxAnisotropy = SamplerState.AnisotropicClamp.MaxAnisotropy; SamplerState.MaxMipLevel = SamplerState.AnisotropicClamp.MaxMipLevel; SamplerState.MipMapLevelOfDetailBias = SamplerState.AnisotropicClamp.MipMapLevelOfDetailBias; DepthStencilState = new DepthStencilState(); DepthStencilState = DepthStencilState.None; } } //////////////////////////////////////////////////////////////////////////// #endregion public class Renderer : Component { #region //// Fields //////////// //////////////////////////////////////////////////////////////////////////// private SpriteBatch sb = null; private DeviceStates states = new DeviceStates(); private BlendingMode bmode = BlendingMode.Default; //////////////////////////////////////////////////////////////////////////// #endregion #region //// Properties //////// //////////////////////////////////////////////////////////////////////////// public virtual SpriteBatch SpriteBatch { get { return sb; } } //////////////////////////////////////////////////////////////////////////// #endregion #region //// Constructors ////// //////////////////////////////////////////////////////////////////////////// public Renderer(Manager manager) : base(manager) { sb = new SpriteBatch(Manager.GraphicsDevice); } //////////////////////////////////////////////////////////////////////////// #endregion #region //// Destructors /////// //////////////////////////////////////////////////////////////////////////// protected override void Dispose(bool disposing) { if (disposing) { if (sb != null) { sb.Dispose(); sb = null; } } base.Dispose(disposing); } //////////////////////////////////////////////////////////////////////////// #endregion #region //// Methods /////////// //////////////////////////////////////////////////////////////////////////// public override void Init() { base.Init(); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void Begin(BlendingMode mode) { bmode = mode; if (mode != BlendingMode.None) { sb.Begin(SpriteSortMode.Immediate, states.BlendState, states.SamplerState, states.DepthStencilState, states.RasterizerState); } else { sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque, states.SamplerState, states.DepthStencilState, states.RasterizerState); } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void End() { sb.End(); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void Draw(Texture2D texture, Rectangle destination, Color color) { if (destination.Width > 0 && destination.Height > 0) { sb.Draw(texture, destination, null, color, 0.0f, Vector2.Zero, SpriteEffects.None, Manager.GlobalDepth); } } //////////////////////////////////////////////////////////////////////////// public virtual void DrawTileTexture(Texture2D texture, Rectangle destination, Color color) { if (destination.Width > 0 && destination.Height > 0) { End(); sb.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, SamplerState.LinearWrap, DepthStencilState.Default, RasterizerState.CullNone); sb.Draw(texture, new Vector2(destination.X,destination.Y), destination, color, 0, Vector2.Zero, 1, SpriteEffects.None, 0); End(); Begin(bmode); } } //////////////////////////////////////////////////////////////////////////// public virtual void Draw(Texture2D texture, Rectangle destination, Rectangle source, Color color) { if (source.Width > 0 && source.Height > 0 && destination.Width > 0 && destination.Height > 0) { sb.Draw(texture, destination, source, color, 0.0f, Vector2.Zero, SpriteEffects.None, Manager.GlobalDepth); } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void Draw(Texture2D texture, int left, int top, Color color) { sb.Draw(texture, new Vector2(left, top), null, color, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, Manager.GlobalDepth); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void Draw(Texture2D texture, int left, int top, Rectangle source, Color color) { if (source.Width > 0 && source.Height > 0) { sb.Draw(texture, new Vector2(left, top), source, color, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, Manager.GlobalDepth); } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(SpriteFont font, string text, int left, int top, Color color) { sb.DrawString(font, text, new Vector2(left, top), color, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, Manager.GlobalDepth); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(SpriteFont font, string text, Rectangle rect, Color color, Alignment alignment) { DrawString(font, text, rect, color, alignment, 0, 0, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(SpriteFont font, string text, Rectangle rect, Color color, Alignment alignment, bool ellipsis) { DrawString(font, text, rect, color, alignment, 0, 0, ellipsis); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect) { DrawString(control, layer, text, rect, true, 0, 0, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, ControlState state) { DrawString(control, layer, text, rect, state, true, 0, 0, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, bool margins) { DrawString(control, layer, text, rect, margins, 0, 0, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, ControlState state, bool margins) { DrawString(control, layer, text, rect, state, margins, 0, 0, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, bool margins, int ox, int oy) { DrawString(control, layer, text, rect, margins, ox, oy, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, ControlState state, bool margins, int ox, int oy) { DrawString(control, layer, text, rect, state, margins, ox, oy, true); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, bool margins, int ox, int oy, bool ellipsis) { DrawString(control, layer, text, rect, control.ControlState, margins, ox, oy, ellipsis); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(Control control, SkinLayer layer, string text, Rectangle rect, ControlState state, bool margins, int ox, int oy, bool ellipsis) { Color col = Color.White; if (layer.Text != null) { if (margins) { Margins m = layer.ContentMargins; rect = new Rectangle(rect.Left + m.Left, rect.Top + m.Top, rect.Width - m.Horizontal, rect.Height - m.Vertical); } if (state == ControlState.Hovered && (layer.States.Hovered.Index != -1)) { col = layer.Text.Colors.Hovered; } else if (state == ControlState.Pressed) { col = layer.Text.Colors.Pressed; } else if (state == ControlState.Focused || (control.Focused && state == ControlState.Hovered && layer.States.Hovered.Index == -1)) { col = layer.Text.Colors.Focused; } else if (state == ControlState.Disabled) { col = layer.Text.Colors.Disabled; } else { col = layer.Text.Colors.Enabled; } if (text != null && text != "") { SkinText font = layer.Text; if (control.TextColor != Control.UndefinedColor && control.ControlState != ControlState.Disabled) col = control.TextColor; DrawString(font.Font.Resource, text, rect, col, font.Alignment, font.OffsetX + ox, font.OffsetY + oy, ellipsis); } } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawString(SpriteFont font, string text, Rectangle rect, Color color, Alignment alignment, int offsetX, int offsetY, bool ellipsis) { if (ellipsis) { const string elli = "..."; int size = (int)Math.Ceiling(font.MeasureString(text).X); if (size > rect.Width) { int es = (int)Math.Ceiling(font.MeasureString(elli).X); for (int i = text.Length - 1; i > 0; i--) { int c = 1; if (char.IsWhiteSpace(text[i - 1])) { c = 2; i--; } text = text.Remove(i, c); size = (int)Math.Ceiling(font.MeasureString(text).X); if (size + es <= rect.Width) { break; } } text += elli; } } if (rect.Width > 0 && rect.Height > 0) { Vector2 pos = new Vector2(rect.Left, rect.Top); Vector2 size = font.MeasureString(text); int x = 0; int y = 0; switch (alignment) { case Alignment.TopLeft: break; case Alignment.TopCenter: x = GetTextCenter(rect.Width, size.X); break; case Alignment.TopRight: x = rect.Width - (int)size.X; break; case Alignment.MiddleLeft: y = GetTextCenter(rect.Height, size.Y); break; case Alignment.MiddleRight: x = rect.Width - (int)size.X; y = GetTextCenter(rect.Height, size.Y); break; case Alignment.BottomLeft: y = rect.Height - (int)size.Y; break; case Alignment.BottomCenter: x = GetTextCenter(rect.Width, size.X); y = rect.Height - (int)size.Y; break; case Alignment.BottomRight: x = rect.Width - (int)size.X; y = rect.Height - (int)size.Y; break; default: x = GetTextCenter(rect.Width, size.X); y = GetTextCenter(rect.Height, size.Y); break; } pos.X = (int)(pos.X + x); pos.Y = (int)(pos.Y + y); DrawString(font, text, (int)pos.X + offsetX, (int)pos.Y + offsetY, color); } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// private static int GetTextCenter(float size1, float size2) { return (int)Math.Ceiling((size1 / 2) - (size2 / 2)); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawLayer(SkinLayer layer, Rectangle rect, Color color, int index) { Size imageSize = new Size(layer.Image.Resource.Width, layer.Image.Resource.Height); Size partSize = new Size(layer.Width, layer.Height); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.TopLeft), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.TopLeft, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.TopCenter), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.TopCenter, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.TopRight), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.TopRight, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.MiddleLeft), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.MiddleLeft, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.MiddleCenter), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.MiddleCenter, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.MiddleRight), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.MiddleRight, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.BottomLeft), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.BottomLeft, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.BottomCenter), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.BottomCenter, index), color); Draw(layer.Image.Resource, GetDestinationArea(rect, layer.SizingMargins, Alignment.BottomRight), GetSourceArea(imageSize, partSize, layer.SizingMargins, Alignment.BottomRight, index), color); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// private static Rectangle GetSourceArea(Size imageSize, Size partSize, Margins margins, Alignment alignment, int index) { Rectangle rect = new Rectangle(); int xc = (int)((float)imageSize.Width / partSize.Width); int yc = (int)((float)imageSize.Height / partSize.Height); int xm = (index) % xc; int ym = (index) / xc; int adj = 1; margins.Left += margins.Left > 0 ? adj : 0; margins.Top += margins.Top > 0 ? adj : 0; margins.Right += margins.Right > 0 ? adj : 0; margins.Bottom += margins.Bottom > 0 ? adj : 0; margins = new Margins(margins.Left, margins.Top, margins.Right, margins.Bottom); switch (alignment) { case Alignment.TopLeft: { rect = new Rectangle((0 + (xm * partSize.Width)), (0 + (ym * partSize.Height)), margins.Left, margins.Top); break; } case Alignment.TopCenter: { rect = new Rectangle((0 + (xm * partSize.Width)) + margins.Left, (0 + (ym * partSize.Height)), partSize.Width - margins.Left - margins.Right, margins.Top); break; } case Alignment.TopRight: { rect = new Rectangle((partSize.Width + (xm * partSize.Width)) - margins.Right, (0 + (ym * partSize.Height)), margins.Right, margins.Top); break; } case Alignment.MiddleLeft: { rect = new Rectangle((0 + (xm * partSize.Width)), (0 + (ym * partSize.Height)) + margins.Top, margins.Left, partSize.Height - margins.Top - margins.Bottom); break; } case Alignment.MiddleCenter: { rect = new Rectangle((0 + (xm * partSize.Width)) + margins.Left, (0 + (ym * partSize.Height)) + margins.Top, partSize.Width - margins.Left - margins.Right, partSize.Height - margins.Top - margins.Bottom); break; } case Alignment.MiddleRight: { rect = new Rectangle((partSize.Width + (xm * partSize.Width)) - margins.Right, (0 + (ym * partSize.Height)) + margins.Top, margins.Right, partSize.Height - margins.Top - margins.Bottom); break; } case Alignment.BottomLeft: { rect = new Rectangle((0 + (xm * partSize.Width)), (partSize.Height + (ym * partSize.Height)) - margins.Bottom, margins.Left, margins.Bottom); break; } case Alignment.BottomCenter: { rect = new Rectangle((0 + (xm * partSize.Width)) + margins.Left, (partSize.Height + (ym * partSize.Height)) - margins.Bottom, partSize.Width - margins.Left - margins.Right, margins.Bottom); break; } case Alignment.BottomRight: { rect = new Rectangle((partSize.Width + (xm * partSize.Width)) - margins.Right, (partSize.Height + (ym * partSize.Height)) - margins.Bottom, margins.Right, margins.Bottom); break; } } return rect; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public static Rectangle GetDestinationArea(Rectangle area, Margins margins, Alignment alignment) { Rectangle rect = new Rectangle(); int adj = 1; margins.Left += margins.Left > 0 ? adj : 0; margins.Top += margins.Top > 0 ? adj : 0; margins.Right += margins.Right > 0 ? adj : 0; margins.Bottom += margins.Bottom > 0 ? adj : 0; margins = new Margins(margins.Left, margins.Top, margins.Right, margins.Bottom); switch (alignment) { case Alignment.TopLeft: { rect = new Rectangle(area.Left + 0, area.Top + 0, margins.Left, margins.Top); break; } case Alignment.TopCenter: { rect = new Rectangle(area.Left + margins.Left, area.Top + 0, area.Width - margins.Left - margins.Right, margins.Top); break; } case Alignment.TopRight: { rect = new Rectangle(area.Left + area.Width - margins.Right, area.Top + 0, margins.Right, margins.Top); break; } case Alignment.MiddleLeft: { rect = new Rectangle(area.Left + 0, area.Top + margins.Top, margins.Left, area.Height - margins.Top - margins.Bottom); break; } case Alignment.MiddleCenter: { rect = new Rectangle(area.Left + margins.Left, area.Top + margins.Top, area.Width - margins.Left - margins.Right, area.Height - margins.Top - margins.Bottom); break; } case Alignment.MiddleRight: { rect = new Rectangle(area.Left + area.Width - margins.Right, area.Top + margins.Top, margins.Right, area.Height - margins.Top - margins.Bottom); break; } case Alignment.BottomLeft: { rect = new Rectangle(area.Left + 0, area.Top + area.Height - margins.Bottom, margins.Left, margins.Bottom); break; } case Alignment.BottomCenter: { rect = new Rectangle(area.Left + margins.Left, area.Top + area.Height - margins.Bottom, area.Width - margins.Left - margins.Right, margins.Bottom); break; } case Alignment.BottomRight: { rect = new Rectangle(area.Left + area.Width - margins.Right, area.Top + area.Height - margins.Bottom, margins.Right, margins.Bottom); break; } } return rect; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public void DrawGlyph(Glyph glyph, Rectangle rect) { Size imageSize = new Size(glyph.Image.Width, glyph.Image.Height); if (!glyph.SourceRect.IsEmpty) { imageSize = new Size(glyph.SourceRect.Width, glyph.SourceRect.Height); } if (glyph.SizeMode == SizeMode.Centered) { rect = new Rectangle((rect.X + (rect.Width - imageSize.Width) / 2) + glyph.Offset.X, (rect.Y + (rect.Height - imageSize.Height) / 2) + glyph.Offset.Y, imageSize.Width, imageSize.Height); } else if (glyph.SizeMode == SizeMode.Normal) { rect = new Rectangle(rect.X + glyph.Offset.X, rect.Y + glyph.Offset.Y, imageSize.Width, imageSize.Height); } else if (glyph.SizeMode == SizeMode.Auto) { rect = new Rectangle(rect.X + glyph.Offset.X, rect.Y + glyph.Offset.Y, imageSize.Width, imageSize.Height); } if (glyph.SourceRect.IsEmpty) { Draw(glyph.Image, rect, glyph.Color); } else { Draw(glyph.Image, rect, glyph.SourceRect, glyph.Color); } } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawLayer(Control control, SkinLayer layer, Rectangle rect) { DrawLayer(control, layer, rect, control.ControlState); } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// public virtual void DrawLayer(Control control, SkinLayer layer, Rectangle rect, ControlState state) { Color c = Color.White; Color oc = Color.White; int i = 0; int oi = -1; SkinLayer l = layer; if (state == ControlState.Hovered && (layer.States.Hovered.Index != -1)) { c = l.States.Hovered.Color; i = l.States.Hovered.Index; if (l.States.Hovered.Overlay) { oc = l.Overlays.Hovered.Color; oi = l.Overlays.Hovered.Index; } } else if (state == ControlState.Focused || (control.Focused && state == ControlState.Hovered && layer.States.Hovered.Index == -1)) { c = l.States.Focused.Color; i = l.States.Focused.Index; if (l.States.Focused.Overlay) { oc = l.Overlays.Focused.Color; oi = l.Overlays.Focused.Index; } } else if (state == ControlState.Pressed) { c = l.States.Pressed.Color; i = l.States.Pressed.Index; if (l.States.Pressed.Overlay) { oc = l.Overlays.Pressed.Color; oi = l.Overlays.Pressed.Index; } } else if (state == ControlState.Disabled) { c = l.States.Disabled.Color; i = l.States.Disabled.Index; if (l.States.Disabled.Overlay) { oc = l.Overlays.Disabled.Color; oi = l.Overlays.Disabled.Index; } } else { c = l.States.Enabled.Color; i = l.States.Enabled.Index; if (l.States.Enabled.Overlay) { oc = l.Overlays.Enabled.Color; oi = l.Overlays.Enabled.Index; } } if (control.Color != Control.UndefinedColor) c = control.Color * (control.Color.A / 255f); DrawLayer(l, rect, c, i); if (oi != -1) { DrawLayer(l, rect, oc, oi); } } //////////////////////////////////////////////////////////////////////////// #endregion } }