Отключить смешивание изображений на PictureBox - PullRequest
0 голосов
/ 16 февраля 2019

В моей программе Windows Forms у меня есть PictureBox.Растровое изображение внутри него маленькое, 5 x 5 пикселей.
При назначении на PictureBox оно становится очень размытым.

Я пытался найти что-то вроде режима наложения, размытия или сглаживания, но мне не повезло.

Image1 Image2

   This is what I want     This is not what I want

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Решение, которое я видел пару раз, состоит в создании переопределяющего класса PictureBox, который имеет InterpolationMode как свойство класса.Тогда все, что вам нужно сделать, это использовать этот класс в пользовательском интерфейсе вместо собственного PictureBox .Net и установить этот режим на NearestNeighbor.

Public Class PixelBox
    Inherits PictureBox

    <Category("Behavior")>
    <DefaultValue(InterpolationMode.NearestNeighbor)>
    Public Property InterpolationMode As InterpolationMode = InterpolationMode.NearestNeighbor

    Protected Overrides Sub OnPaint(pe As PaintEventArgs)
        Dim g As Graphics = pe.Graphics
        g.InterpolationMode = Me.InterpolationMode
        ' Fix half-pixel shift on NearestNeighbor
        If Me.InterpolationMode = InterpolationMode.NearestNeighbor Then _
            g.PixelOffsetMode = PixelOffsetMode.Half
        MyBase.OnPaint(pe)
    End Sub
End Class

Как отмечалось в комментариях для Nearest NeighborВ режиме вам нужно установить PixelOffsetMode на Half.Я, честно говоря, не понимаю, почему они не стали выставлять это, а не делали автоматический выбор во внутреннем процессе рендеринга.

Размер можно контролировать, задав свойство SizeMode элемента управления.Если установить значение Zoom, оно будет автоматически центрироваться и расширяться без ограничения установленного размера элемента управления.

0 голосов
/ 16 февраля 2019

Проблема :
Растровое изображение, размер которого намного меньше размера контейнера, использованного для его отображения, размыто , а в остальном острые края скважиныопределенные области цвета смешиваются бесцеремонно.
Это всего лишь результат применения билинейного фильтра к действительно маленькому изображению (несколько пикселей) при увеличении.

Желаемый результат заключается в том, чтобы вместо этого сохранить исходныйцвет отдельных пикселей при увеличении изображения.

Для достижения этого результата достаточно установить для InterpolationMode объекта Graphics объект

e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor

Этот фильтр, также известный как Point Filter, просто выбирает цвет, ближайший к цвету пикселя, который оценивается.При оценке однородных цветовых областей результатом является один и тот же цвет пикселей для всех пикселей.
Существует только одна проблема - значение по умолчанию для объекта Graphics PixelOffsetMode , которое равно:

e.Graphics.PixelOffsetMode = PixelOffsetMode.None

Когда этот режим активен, внешние пиксели, соответствующие верхней и левой границам изображения (в выборке изображения normal ), рисуются в середине краев прямоугольной области, определяемой какконтейнер (целевой битмап или контекст устройства).

Из-за этого, поскольку исходное изображение является небольшим, а его пиксели значительно увеличены, пиксели первых горизонтальных и вертикальных линий заметно сокращаются пополам.
Это можно решить с помощью другой PixelOffsetMode:

e.Graphics.PixelOffsetMode = PixelOffsetMode.Half

Этот режим перемещает назад положение рендеринга изображения на полпикселя.
Пример изображения результатов может объяснить этолучше:

InterpolationMode NearestNeighbor

      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), мы (почти) вернулись на круги своя.

Простое решение - нарисовать новое растровое изображение непосредственно на поверхности элемента управления и сохранить его на диске, когда / при необходимости.

Это также позволит масштабировать растровое изображение при необходимости без потери качества:

PixelOffsetMode Scale Bitmap

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
...