Проблема :
Растровое изображение, размер которого намного меньше размера контейнера, использованного для его отображения, размыто , а в остальном острые края скважиныопределенные области цвета смешиваются бесцеремонно.
Это всего лишь результат применения билинейного фильтра к действительно маленькому изображению (несколько пикселей) при увеличении.
Желаемый результат заключается в том, чтобы вместо этого сохранить исходныйцвет отдельных пикселей при увеличении изображения.
Для достижения этого результата достаточно установить для InterpolationMode объекта Graphics объект
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
Этот фильтр, также известный как Point Filter
, просто выбирает цвет, ближайший к цвету пикселя, который оценивается.При оценке однородных цветовых областей результатом является один и тот же цвет пикселей для всех пикселей.
Существует только одна проблема - значение по умолчанию для объекта Graphics PixelOffsetMode , которое равно:
e.Graphics.PixelOffsetMode = PixelOffsetMode.None
Когда этот режим активен, внешние пиксели, соответствующие верхней и левой границам изображения (в выборке изображения normal ), рисуются в середине краев прямоугольной области, определяемой какконтейнер (целевой битмап или контекст устройства).
Из-за этого, поскольку исходное изображение является небольшим, а его пиксели значительно увеличены, пиксели первых горизонтальных и вертикальных линий заметно сокращаются пополам.
Это можно решить с помощью другой PixelOffsetMode
:
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half
Этот режим перемещает назад положение рендеринга изображения на полпикселя.
Пример изображения результатов может объяснить этолучше:
Default Filter InterpolationMode InterpolationMode
InterpolationMode NearestNeighbor NearestNeighbor
Bilinear PixelOffsetMode.None PixelOffsetMode.Half
Примечание :
Документы MSNet .Net не описывают параметр PixelOffsetMode
отлично.Вы можете найти 6, по-видимому, разных вариантов.Режимов смещения пикселей на самом деле только два:
PixelOffsetMode.None
(по умолчанию) и PixelOffsetMode.Half
.
PixelOffsetMode.Default
и PixelOffsetMode.HighSpeed
совпадают с PixelOffsetMode.None
.
PixelOffsetMode.HighQuality
совпадает с PixelOffsetMode.Half
.
Читая .Net Docs, похоже, что скорость последствия при выборе одного над другим.Разница на самом деле незначительна.
Документация C ++ по этому вопросу (и GDI + в целом) гораздо более точная и точная, ее следует использовать вместо .Net.
Как продолжить :
Мы могли бы нарисовать маленький исходный битовый образ в новый, больший битовый массив и назначить его для свойства PictureBox.Image
,
Но предположим, что размер PictureBox изменяется в какой-то момент (из-за изменения макета и / или из-за компромиссов DPI Awareness), мы (почти) вернулись на круги своя.
Простое решение - нарисовать новое растровое изображение непосредственно на поверхности элемента управления и сохранить его на диске, когда / при необходимости.
Это также позволит масштабировать растровое изображение при необходимости без потери качества:
Imports System.Drawing
Imports System.Drawing.Drawing2D
Private pixelBitmap As Bitmap = Nothing
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
pixelBitmap = DirectCast(New Bitmap("File Path").Clone(), Bitmap)
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half
e.Graphics.DrawImage(pixelBitmap, GetScaledImageRect(pixelBitmap, DirectCast(sender, Control)))
End Sub
Private Sub PictureBox1_Resize(sender As Object, e As EventArgs) Handles PictureBox1.Resize
PictureBox1.Invalidate()
End Sub
GetScaledImageRect
- это вспомогательный метод, используемый для масштабирования изображения внутри контейнера:
Public Function GetScaledImageRect(image As Image, canvas As Control) As RectangleF
Return GetScaledImageRect(image, canvas.ClientSize)
End Function
Public Function GetScaledImageRect(image As Image, containerSize As SizeF) As RectangleF
Dim imgRect As RectangleF = RectangleF.Empty
Dim scaleFactor As Single = CSng(image.Width / image.Height)
Dim containerRatio As Single = containerSize.Width / containerSize.Height
If containerRatio >= scaleFactor Then
imgRect.Size = New SizeF(containerSize.Height * scaleFactor, containerSize.Height)
imgRect.Location = New PointF((containerSize.Width - imgRect.Width) / 2, 0)
Else
imgRect.Size = New SizeF(containerSize.Width, containerSize.Width / scaleFactor)
imgRect.Location = New PointF(0, (containerSize.Height - imgRect.Height) / 2)
End If
Return imgRect
End Function