在PictureBox上禁用图像混合

时间:2019-02-16 07:35:22

标签: vb.net winforms bitmap picturebox color-blending

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

我尝试查找诸如混合模式,模糊模式或抗锯齿模式之类的东西,但是我没有运气。

Image1 Image2

   This is what I want     This is not what I want

2 个答案:

答案 0 :(得分:5)

问题
模糊了一个位图,该位图的大小比用于显示它的容器小得多,并且本来可以很好地混合颜色定义明确的区域的尖锐边缘。
这只是将双线性滤镜放大后应用于很小的图像(几个像素)的结果。

所需的结果是在放大图像时保持单个像素的原始颜色。

要获得此结果,只需将Graphics对象的InterpolationMode设置为:

e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor

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

e.Graphics.PixelOffsetMode = PixelOffsetMode.None

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

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

e.Graphics.PixelOffsetMode = PixelOffsetMode.Half

此模式后退图片的渲染位置半个像素。
结果的样本图像可以更好地说明这一点:

InterpolationMode NearestNeighbor

      Default Filter         InterpolationMode        InterpolationMode
    InterpolationMode         NearestNeighbor          NearestNeighbor
         Bilinear           PixelOffsetMode.None     PixelOffsetMode.Half

注意
.Net的MSDN文档对PixelOffsetMode参数的描述不太好。您可以找到6个明显不同的选择。像素偏移模式实际上只有两种:
PixelOffsetMode.None (默认)和 PixelOffsetMode.Half

PixelOffsetMode.DefaultPixelOffsetMode.HighSpeedPixelOffsetMode.None相同。
PixelOffsetMode.HighQualityPixelOffsetMode.Half相同。
阅读.Net Docs时,选择其中一项似乎有 speed 含义。差异实际上可以忽略不计。

C++ documentation about this matter(通常是GDI +)更加明确和精确,应该使用它代替.Net。

如何进行

我们可以将较小的源位图绘制到一个新的较大的位图,并将其分配给 PictureBox.Image 属性。

但是,假设PictureBox的大小在某个时候发生了变化(因为布局更改和/或由于DPI意识的折衷),我们(几乎)回到了平方。

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

这还将允许在需要时缩放位图,而不会损失质量:

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

答案 1 :(得分:2)

我已经看过几次的解决方案是制作一个PictureBox的重载类,它具有InterpolationMode作为类属性。然后,您要做的就是在UI上使用此类而不是.Net自己的PictureBox,并将该模式​​设置为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

如注释中所述,对于最近邻居模式,您需要设置 PixelOffsetModeHalf。老实说,我不明白为什么他们不愿在内部渲染过程中将其公开而不是自动选择。

可以通过设置控件的SizeMode属性来控制大小。将其放在Zoom上将使其自动居中并展开,而不会削减控件的设置大小。