diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs index caaf11bebf..bcee2206ef 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/CaretLayer.cs @@ -79,7 +79,7 @@ namespace ICSharpCode.AvalonEdit.Editing caretRectangle.Y - textView.VerticalOffset, caretRectangle.Width, caretRectangle.Height); - drawingContext.DrawRectangle(caretBrush, null, PixelSnapHelpers.ToPixels(r)); + drawingContext.DrawRectangle(caretBrush, null, PixelSnapHelpers.PixelAlign(r)); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs index f2ee1b7947..e041441574 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMargin.cs @@ -37,7 +37,8 @@ namespace ICSharpCode.AvalonEdit.Folding foreach (FoldingMarginMarker m in markers) { m.Measure(availableSize); } - return new Size(SizeFactor * (double)GetValue(TextBlock.FontSizeProperty), 0); + double width = SizeFactor * (double)GetValue(TextBlock.FontSizeProperty); + return new Size(PixelSnapHelpers.RoundToOdd(width), 0); } /// @@ -49,7 +50,7 @@ namespace ICSharpCode.AvalonEdit.Folding double yPos = m.VisualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop) - TextView.VerticalOffset; yPos += (textLine.Height - m.DesiredSize.Height) / 2; double xPos = (finalSize.Width - m.DesiredSize.Width) / 2; - m.Arrange(new Rect(new Point(xPos, yPos), m.DesiredSize)); + m.Arrange(new Rect(PixelSnapHelpers.Round(new Point(xPos, yPos)), m.DesiredSize)); } return base.ArrangeOverride(finalSize); } @@ -84,7 +85,6 @@ namespace ICSharpCode.AvalonEdit.Folding if (fs.StartOffset <= line.LastDocumentLine.Offset + line.LastDocumentLine.Length) { FoldingMarginMarker m = new FoldingMarginMarker { IsExpanded = !fs.IsFolded, - SnapsToDevicePixels = true, VisualLine = line, FoldingSection = fs }; @@ -208,6 +208,10 @@ namespace ICSharpCode.AvalonEdit.Folding /// void DrawFoldLines(DrawingContext drawingContext, Pen[] colors, Pen[] endMarker) { + // Because we are using PenLineCap.Flat (the default), for vertical lines, + // Y coordinates must be on pixel boundaries, whereas the X coordinate must be in the + // middle of a pixel. (and the other way round for horizontal lines) + Size pixelSize = PixelSnapHelpers.GetPixelSize(); double markerXPos = PixelSnapHelpers.PixelAlign(RenderSize.Width / 2); double startY = 0; Pen currentPen = colors[0]; @@ -216,12 +220,12 @@ namespace ICSharpCode.AvalonEdit.Folding foreach (TextLine tl in vl.TextLines) { if (endMarker[tlNumber] != null) { double visualPos = GetVisualPos(vl, tl); - drawingContext.DrawLine(endMarker[tlNumber], new Point(markerXPos, visualPos), new Point(RenderSize.Width, visualPos)); + drawingContext.DrawLine(endMarker[tlNumber], new Point(markerXPos - pixelSize.Width / 2, visualPos), new Point(RenderSize.Width, visualPos)); } if (colors[tlNumber + 1] != currentPen) { double visualPos = GetVisualPos(vl, tl); if (currentPen != null) { - drawingContext.DrawLine(currentPen, new Point(markerXPos, startY), new Point(markerXPos, visualPos)); + drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, visualPos - pixelSize.Height / 2)); } currentPen = colors[tlNumber + 1]; startY = visualPos; @@ -230,14 +234,14 @@ namespace ICSharpCode.AvalonEdit.Folding } } if (currentPen != null) { - drawingContext.DrawLine(currentPen, new Point(markerXPos, startY), new Point(markerXPos, RenderSize.Height)); + drawingContext.DrawLine(currentPen, new Point(markerXPos, startY + pixelSize.Height / 2), new Point(markerXPos, RenderSize.Height)); } } double GetVisualPos(VisualLine vl, TextLine tl) { double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset; - return Math.Round(pos) + 0.5; + return PixelSnapHelpers.PixelAlign(pos); } int GetTextLineIndexFromOffset(List textLines, int offset) diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs index ab3bf6c065..1154a495e0 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Folding/FoldingMarginMarker.cs @@ -12,6 +12,7 @@ using System.Windows.Input; using System.Windows.Media; using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.AvalonEdit.Utils; namespace ICSharpCode.AvalonEdit.Folding { @@ -40,7 +41,7 @@ namespace ICSharpCode.AvalonEdit.Folding if (!e.Handled) { if (e.ChangedButton == MouseButton.Left) { IsExpanded = !IsExpanded; - e.Handled = true; + e.Handled = true; } } } @@ -50,23 +51,26 @@ namespace ICSharpCode.AvalonEdit.Folding protected override Size MeasureCore(Size availableSize) { double size = MarginSizeFactor * FoldingMargin.SizeFactor * (double)GetValue(TextBlock.FontSizeProperty); - size = Math.Round(size); - if (Math.Abs((size % 2) - 1) < 0.001) { - size -= 1; - } + size = PixelSnapHelpers.RoundToOdd(size); return new Size(size, size); } protected override void OnRender(DrawingContext drawingContext) { Pen blackPen = new Pen(Brushes.Black, 1); - Rect rect = new Rect(new Point(0.5, 0.5), this.RenderSize); + blackPen.StartLineCap = PenLineCap.Square; + blackPen.EndLineCap = PenLineCap.Square; + Size pixelSize = PixelSnapHelpers.GetPixelSize(); + Rect rect = new Rect(pixelSize.Width / 2, + pixelSize.Height / 2, + this.RenderSize.Width - pixelSize.Width, + this.RenderSize.Height - pixelSize.Height); drawingContext.DrawRectangle(Brushes.White, IsMouseDirectlyOver ? blackPen : new Pen(Brushes.Gray, 1), rect); double middleX = rect.Left + rect.Width / 2; double middleY = rect.Top + rect.Height / 2; - double space = Math.Round(rect.Width / 8) + 1; + double space = PixelSnapHelpers.Round(rect.Width / 8) + pixelSize.Width; drawingContext.DrawLine(blackPen, new Point(rect.Left + space, middleY), new Point(rect.Right - space, middleY)); diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs index cc9e3d8071..4a9bcdc215 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Rendering/BackgroundGeometryBuilder.cs @@ -44,8 +44,10 @@ namespace ICSharpCode.AvalonEdit.Rendering /// public void AddSegment(TextView textView, ISegment segment) { - foreach (Rect r in GetRectsForSegment(textView, segment)) - AddRectangle(r.Left, r.Top, r.Right, r.Bottom); + foreach (Rect r in GetRectsForSegment(textView, segment)) { + Rect roundedRect = PixelSnapHelpers.PixelAlign(r); + AddRectangle(roundedRect.Left, roundedRect.Top, roundedRect.Right, roundedRect.Bottom); + } } /// diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs index 1db689938c..36ba025bc9 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/PixelSnapHelpers.cs @@ -12,18 +12,67 @@ namespace ICSharpCode.AvalonEdit.Utils { static class PixelSnapHelpers { + public static Size GetPixelSize() + { + return new Size(1, 1); + } + + /// + /// Aligns val on the next middle of a pixel. + /// public static double PixelAlign(double val) { + // 0 -> 0.5 + // 0.1 -> 0.5 + // 0.5 -> 0.5 + // 0.9 -> 0.5 + // 1 -> 1.5 return Math.Round(val + 0.5) - 0.5; } - public static Rect ToPixels(Rect rect) + /// + /// Aligns the borders of rect on the middles of pixels. + /// + public static Rect PixelAlign(Rect rect) { rect.X = PixelAlign(rect.X); rect.Y = PixelAlign(rect.Y); - rect.Width = Math.Round(rect.Width); - rect.Height = Math.Round(rect.Height); + rect.Width = Round(rect.Width); + rect.Height = Round(rect.Height); return rect; } + + public static Point Round(Point val) + { + return new Point(Round(val.X), Round(val.Y)); + } + + /// + /// Rounds val to a whole number of pixels. + /// + public static double Round(double val) + { + return Math.Round(val); + } + + /// + /// Rounds val to an even number of pixels. + /// + public static double RoundToEven(double val) + { + // 0 -> 0 + // 1 -> 2 + // 2 -> 2 + // 3 -> 4 + return Math.Round(val / 2) * 2; + } + + /// + /// Rounds val to an odd number of pixels. + /// + public static double RoundToOdd(double val) + { + return RoundToEven(val - 1) + 1; + } } }