禁用PictureBox上的图像混合

The*_*une 4 vb.net bitmap picturebox winforms color-blending

在我的Windows窗体程序中,我有一个PictureBox.里面的位图图像很小,5 x 5像素.
当分配给a时PictureBox,它变得非常模糊.

我尝试找到混合模式,模糊模式或抗锯齿模式之类的东西,但我没有运气.

此搜索 镜像2

   This is what I want     This is not what I want
Run Code Online (Sandbox Code Playgroud)

Jim*_*imi 6

问题所在:
位图的尺寸远小于用于显示它的容器,它很模糊,并且明确定义的颜色区域的锐利边缘被毫不客气地混合.
这只是在放大时应用于非常小的图像(几个像素)的双线性滤波器的结果.

期望的结果是在图像被放大时保持单个像素的原始颜色.

要实现此结果,将Graphics对象的InterpolationMode设置为:

e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
Run Code Online (Sandbox Code Playgroud)

此过滤器也称为Point Filter,仅选择与正在评估的像素颜色最接近的颜色.在评估均匀的颜色区域时,结果是所有像素的像素颜色相同.
只有一个问题,即Graphics对象的PixelOffsetMode的默认值,它是:

e.Graphics.PixelOffsetMode = PixelOffsetMode.None
Run Code Online (Sandbox Code Playgroud)

在此模式激活的情况下,对应于图像的顶部和左侧边框的外部像素(在正常图像采样中)绘制在由容器定义的矩形区域的边缘的中间(目标位图或设备上下文) .

因此,由于源图像很小并且其像素被放大很多,所以第一水平线和垂直线的像素被明显地切成两半.
这可以使用另一个 来解决PixelOffsetMode:

e.Graphics.PixelOffsetMode = PixelOffsetMode.Half
Run Code Online (Sandbox Code Playgroud)

此模式图像的渲染位置移回半个像素.
结果的示例图像可以更好地解释这一点:

InterpolationMode NearestNeighbor

      Default Filter         InterpolationMode        InterpolationMode
    InterpolationMode         NearestNeighbor          NearestNeighbor
         Bilinear           PixelOffsetMode.None     PixelOffsetMode.Half
Run Code Online (Sandbox Code Playgroud)

注意:
.Net的MSDN Docs不能PixelOffsetMode很好地描述参数.你可以找到6种,显然不同的选择.Pixel Offset模式实际上只有两个:(
PixelOffsetMode.None默认值)和PixelOffsetMode.Half.

PixelOffsetMode.DefaultPixelOffsetMode.HighSpeed是一样的PixelOffsetMode.None.
PixelOffsetMode.HighQuality是一样的PixelOffsetMode.Half.
阅读.Net Docs,当选择一个而不是另一个时,似乎会有速度影响.差异实际上可以忽略不计.

关于这个问题C++文档(以及一般的GDI +)更加明确和精确,应该使用它而不是.Net文件.

如何进行:

我们可以将小源Bitmap绘制到一个新的更大的Bitmap并将其分配给一个PictureBox.Image属性.

但是,假设PictureBox大小在某些时候发生变化(因为布局发生变化和/或因为DPI意识受到影响),我们(几乎)回到了第一个方向.

一个简单的解决方案是直接在控件的表面上绘制新的Bitmap,并在必要时将其保存到光盘.

这也可以在需要时缩放Bitmap而不会降低质量:

PixelOffsetMode缩放位图

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)