public static void DrawMessage(object graphics, Common.Message message, int xOffset, int yOffset, Selection selection, int currentLine, bool drawText, List <GifEmoteState> gifEmotesOnScreen = null, bool allowMessageSeperator = true) { message.X = xOffset; message.Y = yOffset; var g = (Graphics)graphics; var spaceWidth = GuiEngine.Current.MeasureStringSize(g, FontType.Medium, " ").Width; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; g.InterpolationMode = InterpolationMode.HighQualityBicubic; message.X = xOffset; var textColor = App.ColorScheme.Text; Brush highlightBrush = null; if (message.HasAnyHighlightType(HighlightType.SearchResult)) { highlightBrush = App.ColorScheme.ChatBackgroundSearchResult; } else if (message.HasAnyHighlightType(HighlightType.Highlighted)) { highlightBrush = App.ColorScheme.ChatBackgroundHighlighted; } else if (message.HasAnyHighlightType(HighlightType.UsernameHighlighted)) { highlightBrush = App.ColorScheme.ChatBackgroundUsernameHighlighted; } else if (message.HasAnyHighlightType(HighlightType.Resub)) { highlightBrush = App.ColorScheme.ChatBackgroundResub; } else if (message.HasAnyHighlightType(HighlightType.Whisper)) { highlightBrush = App.ColorScheme.ChatBackgroundWhisper; } else if (message.HasAnyHighlightType(HighlightType.HighlightedMessage)) { highlightBrush = App.ColorScheme.ChatBackgroundHighlightedMessage; } if (highlightBrush != null) { g.FillRectangle(highlightBrush, 0, yOffset, g.ClipBounds.Width, message.Height); } g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default; for (var i = 0; i < message.Words.Count; i++) { var word = message.Words[i]; if (word.Type == SpanType.Text) { if (drawText) { var font = Fonts.GetFont(word.Font); var color = textColor; if (word.Color.HasValue) { var hsl = word.Color.Value; if (App.ColorScheme.IsLightTheme) { if (hsl.Luminosity > 0.5f) { hsl = hsl.WithLuminosity(0.5f); } } else { if (hsl.Luminosity < 0.3f) { hsl = hsl.WithLuminosity(0.3f); } } float r, _g, b; hsl.ToRGB(out r, out _g, out b); color = Color.FromArgb((int)(r * 255), (int)(_g * 255), (int)(b * 255)); } if (word.SplitSegments == null) { TextRenderer.DrawText(g, (string)word.Value, font, new Point(xOffset + word.X, yOffset + word.Y), color, App.DefaultTextFormatFlags); } else { var segments = word.SplitSegments; for (var x = 0; x < segments.Length; x++) { TextRenderer.DrawText(g, segments[x].Item1, font, new Point(xOffset + segments[x].Item2.X, yOffset + segments[x].Item2.Y), color, App.DefaultTextFormatFlags); } } } } else if (word.Type == SpanType.LazyLoadedImage) { var emote = (LazyLoadedImage)word.Value; var img = (ChatterinoImage)emote.Image; if (img != null) { try { lock (img) { img.DrawImage(g, word.X + xOffset, word.Y + yOffset, word.Width, word.Height); } } catch { } } } } Action <int, bool, LazyLoadedImage> addWordToGifList = (i, selected, emote) => { var word = message.Words[i]; var words = new List <Word>(); for (var j = i - 1; j >= 0; j--) { if (message.Words[j].Intersects(word)) { words.Insert(0, message.Words[j]); } else { break; } } words.Add(word); for (var j = i + 1; j < message.Words.Count; j++) { if (message.Words[j].Intersects(word)) { words.Add(message.Words[j]); } else { break; } } gifEmotesOnScreen?.Add(new GifEmoteState(word.X + xOffset, word.Y + yOffset, word.Width, word.Height, words, selected, message.HighlightType, message.Disabled, yOffset, xOffset)); GuiEngine.Current.GifEmotesOnScreen.Add(emote); }; if (selection != null && !selection.IsEmpty && selection.First.MessageIndex <= currentLine && selection.Last.MessageIndex >= currentLine) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; var first = selection.First; var last = selection.Last; for (var i = 0; i < message.Words.Count; i++) { var word = message.Words[i]; if ((currentLine != first.MessageIndex || i >= first.WordIndex) && (currentLine != last.MessageIndex || i <= last.WordIndex)) { if (word.Type == SpanType.Text) { for (var j = 0; j < (word.SplitSegments?.Length ?? 1); j++) { if ((first.MessageIndex == currentLine && first.WordIndex == i && first.SplitIndex > j) || (last.MessageIndex == currentLine && last.WordIndex == i && last.SplitIndex < j)) { continue; } var split = word.SplitSegments?[j]; var text = split?.Item1 ?? (string)word.Value; var rect = split?.Item2 ?? new CommonRectangle(word.X, word.Y, word.Width, word.Height); var textLength = text.Length; var offset = (first.MessageIndex == currentLine && first.SplitIndex == j && first.WordIndex == i) ? first.CharIndex : 0; var length = ((last.MessageIndex == currentLine && last.SplitIndex == j && last.WordIndex == i) ? last.CharIndex : textLength) - offset; if (offset == 0 && length == text.Length) { g.FillRectangle(_selectionBrush, rect.X + xOffset, rect.Y + yOffset, GuiEngine.Current.MeasureStringSize(App.UseDirectX ? null : g, word.Font, text) .Width + spaceWidth - 1, rect.Height); } else if (offset == text.Length) { g.FillRectangle(_selectionBrush, rect.X + xOffset + rect.Width, rect.Y + yOffset, spaceWidth, rect.Height); } else { g.FillRectangle(_selectionBrush, rect.X + xOffset + (offset == 0 ? 0 : GuiEngine.Current.MeasureStringSize(App.UseDirectX ? null : g, word.Font, text.Remove(offset)).Width), rect.Y + yOffset, GuiEngine.Current.MeasureStringSize(App.UseDirectX ? null : g, word.Font, text.Substring(offset, length)).Width + ((last.MessageIndex > currentLine || last.SplitIndex > j || last.WordIndex > i) ? spaceWidth : 0), rect.Height); } } } //else if (word.Type == SpanType.Image) //{ // var textLength = 2; // var offset = (first.MessageIndex == currentLine && first.WordIndex == i) // ? first.CharIndex // : 0; // var length = ((last.MessageIndex == currentLine && last.WordIndex == i) // ? last.CharIndex // : textLength) - offset; // g.FillRectangle(_selectionBrush, word.X + xOffset + (offset == 0 ? 0 : word.Width), // word.Y + yOffset, // (offset == 0 ? word.Width : 0) + (offset + length == 2 ? spaceWidth : 0) - 1, // word.Height); //} else if (word.Type == SpanType.LazyLoadedImage) { var textLength = 2; var offset = (first.MessageIndex == currentLine && first.WordIndex == i) ? first.CharIndex : 0; var length = ((last.MessageIndex == currentLine && last.WordIndex == i) ? last.CharIndex : textLength) - offset; var emote = (LazyLoadedImage)word.Value; if (emote.IsAnimated) { addWordToGifList(i, true, emote); } else { g.FillRectangle(_selectionBrush, word.X + xOffset, word.Y + yOffset, word.Width + spaceWidth - 1, word.Height); } } } else { if (word.Type == SpanType.LazyLoadedImage) { var emote = (LazyLoadedImage)word.Value; if (emote.IsAnimated) { addWordToGifList(i, true, emote); } } } } } else { for (int i = 0; i < message.Words.Count; i++) { var word = message.Words[i]; if (word.Type == SpanType.LazyLoadedImage) { var emote = (LazyLoadedImage)word.Value; if (emote.IsAnimated) { addWordToGifList(i, false, emote); } } } } if (allowMessageSeperator && AppSettings.ChatSeperateMessages) { g.DrawLine(App.ColorScheme.ChatMessageSeperatorBorder, 0, yOffset + 1, message.Width + 128, yOffset + 1); g.DrawLine(App.ColorScheme.ChatMessageSeperatorBorderInner, 0, yOffset, message.Width + 128, yOffset); } }
public CommonSize MeasureStringSize(object graphics, FontType font, string text) { var isGdi = graphics is Graphics; var sizeCache = (isGdi ? gdiSizeCaches : dwSizeCaches).GetOrAdd(font, f => { int lineHeight; if (isGdi) { lineHeight = TextRenderer.MeasureText((Graphics)graphics, "X", Fonts.GetFont(font), Size.Empty, App.DefaultTextFormatFlags).Height; } else { using (var layout = new SharpDX.DirectWrite.TextLayout(Fonts.Factory, "X", Fonts.GetTextFormat(font), 1000000, 1000000)) { var metrics = layout.Metrics; lineHeight = (int)metrics.Height; //lineHeight = (int)Math.Ceiling(Fonts.GetTextFormat(font).FontSize); } } return(Tuple.Create(new ConcurrentDictionary <string, CommonSize>(), new ConcurrentStack <string>(), lineHeight)); }); return(sizeCache.Item1.GetOrAdd(text, s => { if (sizeCache.Item2.Count >= sizeCacheStackLimit) { string value; if (sizeCache.Item2.TryPop(out value)) { CommonSize _s; sizeCache.Item1.TryRemove(value, out _s); } } sizeCache.Item2.Push(s); if (isGdi) { var size = TextRenderer.MeasureText((IDeviceContext)graphics, text, Fonts.GetFont(font), Size.Empty, App.DefaultTextFormatFlags); return new CommonSize(size.Width, sizeCache.Item3); } else { using (var layout = new SharpDX.DirectWrite.TextLayout(Fonts.Factory, text, Fonts.GetTextFormat(font), 1000000, 1000000)) { var metrics = layout.Metrics; return new CommonSize((int)metrics.WidthIncludingTrailingWhitespace, sizeCache.Item3); } } })); }