// return true if control needs to be redrawn public bool CalculateBounds(object graphics, int width, bool enableHatEmotes) { var emotesChanged = EmoteBoundsChanged; var redraw = false; var mediumTextLineHeight = GuiEngine.Current.MeasureStringSize(null, FontType.Medium, "X").Height; if (Width != width) { Width = width; redraw = true; } // check if any words need to be recalculated if (emotesChanged || measureText || _measureImages) { foreach (var word in Words) { if (word.Type == SpanType.Text) { if (measureText) { var size = GuiEngine.Current.MeasureStringSize(graphics, word.Font, (string)word.Value); word.Width = size.Width; word.Height = size.Height; } } else if (word.Type == SpanType.LazyLoadedImage) { if (emotesChanged || _measureImages) { var emote = word.Value as LazyLoadedImage; var image = emote?.Image; if (image == null) { word.Width = word.Height = 16; } else { var size = GuiEngine.Current.GetImageSize(image); double w = size.Width, h = size.Height; if (emote.IsEmote) { if (AppSettings.EmoteScaleByLineHeight) { w = size.Width * ((float)mediumTextLineHeight / size.Height); h = mediumTextLineHeight; } else { w *= emote.Scale; h *= emote.Scale; } w = w * AppSettings.EmoteScale; h = h * AppSettings.EmoteScale; } word.Width = (int)w; word.Height = (int)h; } } } } measureText = false; _measureImages = false; redraw = true; } if (redraw) { var x = 0; var y = 4; var lineStartIndex = 0; var spaceWidth = GuiEngine.Current.MeasureStringSize(graphics, FontType.Medium, " ").Width; var i = 0; Func <int> fixCurrentLineHeight = () => { var lineHeight = 0; for (var j = lineStartIndex; j < i; j++) { var h = Words[j].Height; lineHeight = h > lineHeight ? h : lineHeight; } for (var j = lineStartIndex; j < i; j++) { var word = Words[j]; if (j == lineStartIndex && word.Type == SpanType.Text && word.SplitSegments != null) { var segment = word.SplitSegments[word.SplitSegments.Length - 1]; var rec = segment.Item2; word.SplitSegments[word.SplitSegments.Length - 1] = Tuple.Create(segment.Item1, new CommonRectangle(rec.X, rec.Y + lineHeight - word.Height, rec.Width, rec.Height)); } else { word.Y += lineHeight - word.Height; } } return(lineHeight); }; for (; i < Words.Count; i++) { var word = Words[i]; word.SplitSegments = null; Margin margin = null; if (word.Type == SpanType.LazyLoadedImage && enableHatEmotes && (i > 0 && Words[i - 1].Type != SpanType.Text)) { var img = (LazyLoadedImage)word.Value; if (img.IsHat) { #warning emote size x -= word.Width + 2; } else if (img.Margin != null) { margin = img.Margin; } } // word wrapped text if (word.Width > width && word.Type == SpanType.Text && ((string)word.Value).Length > 2) { y += fixCurrentLineHeight(); lineStartIndex = i; word.X = 0; word.Y = y; var text = (string)word.Value; var startIndex = 0; var items = new List <Tuple <string, CommonRectangle> >(); string s; var widths = word.CharacterWidths; // calculate word widths if (widths == null) { widths = new int[text.Length]; var lastW = 0; for (var j = 0; j < text.Length; j++) { var w = GuiEngine.Current.MeasureStringSize(graphics, word.Font, text.Remove(j)).Width; widths[j] = w - lastW; lastW = w; } word.CharacterWidths = widths; } // create word splits { var w = widths[0]; for (var j = 1; j < text.Length; j++) { w += widths[j]; if (w > width - spaceWidth - spaceWidth - spaceWidth) { items.Add(Tuple.Create(text.Substring(startIndex, j - startIndex), new CommonRectangle(0, y, w, word.Height))); startIndex = j; y += word.Height; w = 0; } } s = text.Substring(startIndex); for (var j = startIndex; j < text.Length; j++) { w += widths[j]; } items.Add(Tuple.Create(s, new CommonRectangle(0, y, w, word.Height))); } x = GuiEngine.Current.MeasureStringSize(graphics, word.Font, s).Width + spaceWidth; if (items.Count > 1) { word.SplitSegments = items.ToArray(); } } // word in new line else if (word.Width > width - x) { y += fixCurrentLineHeight(); word.X = 0 + (margin?.Left ?? 0); word.Y = y + (margin?.Top ?? 0); x = word.Width + spaceWidth + (margin == null ? 0 : (margin.Left + margin.Right)); lineStartIndex = i; } // word fits in current line else { word.X = x + (margin?.Left ?? 0); word.Y = y + (margin?.Top ?? 0); x += word.Width + spaceWidth + (margin == null ? 0 : (margin.Left + margin.Right)); } } y += fixCurrentLineHeight(); Height = y + 4; } if (redraw) { buffer = null; } return(redraw); }
public static IEnumerable <LazyLoadedImage> GetFfzEmoteFromDynamic(dynamic d, bool global) { List <LazyLoadedImage> emotes = new List <LazyLoadedImage>(); foreach (var emote in d) { var name = emote["name"]; dynamic urls = emote["urls"]; int maxScale = 1; var urlX1 = "https:" + urls["1"]; string urlX2 = null; if (urls.ContainsKey("2")) { urlX2 = "https:" + urls["2"]; maxScale = 2; } string urlX4 = null; if (urls.ContainsKey("4")) { urlX4 = "https:" + urls["4"]; maxScale = 4; } var _scale = AppSettings.EmoteScale > 2 ? 4 : (AppSettings.EmoteScale > 1 ? 2 : 1); string url; if (maxScale >= 2 && _scale == 2) { _scale = 2; url = urlX2; } else if (maxScale == 4 && _scale == 4) { _scale = 4; url = urlX4; } else { _scale = 1; url = urlX1; } var scale = 1.0 / _scale; string margins = emote["margins"]; Margin margin = null; if (!string.IsNullOrEmpty(margins)) { var S = margins.Split(' '); if (S.Length == 4) { try { var ints = S.Select(x => { var index = x.Length; for (var i = 0; i < x.Length; i++) { if (x[i] != '-' && (x[i] < '0' || x[i] > '9')) { index = i - 1; } } return(int.Parse(index == x.Length ? x : x.Remove(index))); }).ToArray(); margin = new Margin(ints[0], ints[1], ints[2], ints[3]); } catch { } } } emotes.Add(new LazyLoadedImage { Name = name, Url = url, Scale = scale, Margin = margin, Tooltip = name + $"\nFrankerFaceZ {(global ? "Global" : "Channel")} Emote", IsEmote = true }); } return(emotes); }