private CommentUI MakeCommentUI(Comment comment) { CommentUI ui = new CommentUI() { DataContext = comment }; return(ui); }
private int CalcAndRegisterCommentVerticalPosition(CommentUI commentUI, CommentRenderFrameData frame) { const double TextSizeToMargin = 0.425; // コメントの縦位置ごとの「空き段」を管理するリストを探す List <CommentUI> verticalAlignList; VerticalAlignment?_valign = (commentUI.DataContext as Comment).VAlign; int?commentVerticalPos = null; int totalHeight = 0; // TrimFromBaselineを指定しているため、下コメントに余白を追加 if (_valign.HasValue && _valign.Value == VerticalAlignment.Bottom) { totalHeight += BottomCommentMargin; } else { // 一番上の余白、上にコメントがこないので半分(* 0.5)として計算 totalHeight += (int)(commentUI.DesiredSize.Height * TextSizeToMargin * 0.5); } // 流れるコメント用の計算パラメータ // for文の中だと何度も計算する場合があるので // forスコープの外に出してます var currentVpos = frame.CurrentVpos; var canvasWidth = frame.CanvasWidth; var displayDuration = GetCommentDisplayDurationVposUnit(); var canvasByCommentWidthRatio = canvasWidth / (commentUI.DesiredSize.Width + canvasWidth); var reachToLeftTime = (uint)Math.Floor(displayDuration * canvasByCommentWidthRatio); var currentTimeBaseReachToLeftTime = commentUI.CommentData.VideoPosition + reachToLeftTime; var displayEndTime = commentUI.CommentData.EndPosition; if (_valign.HasValue && _valign.Value == VerticalAlignment.Bottom) { verticalAlignList = BottomAlignNextVerticalPosition; for (int i = 0; i < verticalAlignList.Count; ++i) { var next = verticalAlignList[i]; if (next == null) { commentVerticalPos = (int)this.ActualHeight - (int)commentUI.DesiredSize.Height - totalHeight; verticalAlignList[i] = commentUI; } else { totalHeight += (int)next.DesiredSize.Height + CommentVerticalMargin + (int)(next.DesiredSize.Height * TextSizeToMargin); } } if (!commentVerticalPos.HasValue) { commentVerticalPos = (int)this.ActualHeight - (int)commentUI.DesiredSize.Height - totalHeight; verticalAlignList.Add(commentUI); } } else if (_valign.HasValue && _valign.Value == VerticalAlignment.Top) { // 上コメ verticalAlignList = TopAlignNextVerticalPosition; for (int i = 0; i < verticalAlignList.Count; ++i) { var next = verticalAlignList[i]; if (next == null) { verticalAlignList[i] = commentUI; commentVerticalPos = totalHeight; } else { totalHeight += (int)next.DesiredSize.Height + CommentVerticalMargin + (int)(next.DesiredSize.Height * TextSizeToMargin); } } if (!commentVerticalPos.HasValue) { commentVerticalPos = totalHeight; verticalAlignList.Add(commentUI); } } else { // 流れるコメント verticalAlignList = NextVerticalPosition; for (int i = 0; i < verticalAlignList.Count; ++i) { var next = verticalAlignList[i]; if (next == null) { // 流れるコメントを前のコメントと被らないようにする // 前コメントの表示完了時間と追加したいコメントの表示完了時間を比較し // 追加したいコメントの表示完了時間 // 前のコメントがない場合は、常に追加可能 if (LastCommentDisplayEndTime.Count <= i) { verticalAlignList[i] = commentUI; commentVerticalPos = totalHeight; LastCommentDisplayEndTime.Add(new LastStreamedComment() { EndTime = Math.Max(displayEndTime + 5, 0), Comment = commentUI }); break; } // 前のコメントが有る場合、 // 前コメの表示終了時間より後に終わる場合はコメ追加可能 else if (LastCommentDisplayEndTime[i].EndTime < currentTimeBaseReachToLeftTime) { verticalAlignList[i] = commentUI; commentVerticalPos = totalHeight; LastCommentDisplayEndTime[i] = new LastStreamedComment() { EndTime = Math.Max(displayEndTime + 5, 0), Comment = commentUI }; break; } else { var prevComment = LastCommentDisplayEndTime[i].Comment; totalHeight += (int)prevComment.DesiredSize.Height + CommentVerticalMargin + (int)(prevComment.DesiredSize.Height * TextSizeToMargin); //Debug.WriteLine("前コメと衝突を回避 " + prevComment.CommentData.CommentText); } } else { totalHeight += (int)next.DesiredSize.Height + CommentVerticalMargin + (int)(next.DesiredSize.Height * TextSizeToMargin); } } if (!commentVerticalPos.HasValue) { commentVerticalPos = totalHeight; verticalAlignList.Add(commentUI); } } return(commentVerticalPos.Value); }
private void OnUpdate() { var frame = GetRenderFrameData(); // 非表示時は処理を行わない if (frame.Visibility == Visibility.Collapsed) { if (RenderComments.Count > 0) { foreach (var renderComment in RenderComments.ToArray()) { RenderComments.Remove(renderComment.Key); CommentCanvas.Children.Remove(renderComment.Value); renderComment.Value.DataContext = null; } } return; } // コメントの上下位置を管理するリストを更新 UpdateCommentVerticalPositionList(frame.CurrentVpos); // 表示すべきコメントを抽出して、表示対象として未登録のコメントを登録処理する var displayComments = GetDisplayCommentsOnCurrentVPos(frame.CurrentVpos, frame.CommentDisplayDuration); // Debug.WriteLine("comment: " + displayComments.Count()); // コメントの表示位置決定(直列実行) foreach (var comment in displayComments) { if (!comment.IsVisible) { continue; } CommentUI renderComment = RenderComments.ContainsKey(comment) ? RenderComments[comment] : null; bool isNeedCreateCommentUI = renderComment == null; if (isNeedCreateCommentUI) { renderComment = new CommentUI(); // フォントサイズの計算 // 画面サイズの10分の1*ベーススケール*フォントスケール var baseSize = frame.CanvasHeight * 0.1; const float PixelToPoint = 0.75f; var scaledFontSize = baseSize * frame.FontScale * comment.FontScale * PixelToPoint; comment.FontSize = (uint)Math.Ceiling(scaledFontSize); // フォントの影のオフセット量 comment.TextBGOffset = Math.Floor(FontSize * TextBGOffsetBias); // コメントの終了位置を更新 comment.EndPosition = comment.VideoPosition + frame.CommentDisplayDuration; // コメントカラー if (comment.Color == null) { comment.RealColor = frame.CommentDefaultColor; } else { comment.RealColor = comment.Color.Value; } // コメント背景の色を求める comment.BackColor = GetShadowColor(comment.RealColor); renderComment.DataContext = comment; renderComment.Visibility = Visibility.Visible; // 表示対象に登録 RenderComments.Add(comment, renderComment); CommentCanvas.Children.Add(renderComment); // コメントの表示サイズを得るために強制更新 renderComment.UpdateLayout(); // コメントを配置可能な高さを取得 var verticalPos = CalcAndRegisterCommentVerticalPosition(renderComment, frame); // コメントが画面の中に収まっている場合は表示 // 少しでも見切れる場合は非表示 if (verticalPos < 0 || (verticalPos + renderComment.DesiredSize.Height) > frame.CanvasHeight) { renderComment.Visibility = Visibility.Collapsed; // Debug.WriteLine("hide comment : " + comment.CommentText); } else { // コメントの縦の表示位置を設定 Canvas.SetTop(renderComment, verticalPos); if (comment.VAlign == VerticalAlignment.Bottom || comment.VAlign == VerticalAlignment.Top) { var left = frame.HalfCanvasWidth - (int)(renderComment.DesiredSize.Width * 0.5); Canvas.SetLeft(renderComment, left); Debug.WriteLine($"V={verticalPos}: [{renderComment.CommentData.CommentText}]"); } renderComment.Update(frame.CanvasWidth, frame.CurrentVpos); } } // CommentUIの表示位置を更新 if (!comment.VAlign.HasValue || comment.VAlign == VerticalAlignment.Center) { if (renderComment.Visibility == Visibility.Visible) { renderComment.Update(frame.CanvasWidth, frame.CurrentVpos); Canvas.SetLeft(renderComment, frame.CanvasWidth - renderComment.HorizontalPosition); } } } // 表示区間をすぎたコメントを表示対象から削除 var removeTargets = RenderComments .Where(x => CommentIsEndDisplay(x.Key, frame.CurrentVpos) || x.Key.IsNGComment) .Select(x => x.Value) .ToArray(); foreach (var renderComment in removeTargets) { RenderComments.Remove(renderComment.CommentData); CommentCanvas.Children.Remove(renderComment); renderComment.CommentData = null; } }