winforms 禁用PictureBox上的图像混合

eblbsuwk  于 2023-04-12  发布在  其他
关注(0)|答案(2)|浏览(132)

在我的Windows窗体程序中,我有一个PictureBox,它包含一个小图像,5 x 5像素。
将此位图指定给PictureBox.Image属性时,它会变得非常模糊。
我试图找到一些像混合模式,模糊模式,或抗锯齿模式,但我没有运气。

This is what I want     This is not what I want
ndasle7k

ndasle7k1#

问题

一个位图,如果它的大小比用来显示它的容器小得多,那么它就会变得模糊不清,而且定义明确的颜色区域的尖锐边缘也会被随意地混合。
这只是放大时应用于非常小的图像(几个像素)的双线性滤镜的结果。
所需的结果是在放大图像时保持单个像素的原始颜色。
要实现此结果,只需将Graphics对象的InterpolationMode设置为:

e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor

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

e.Graphics.PixelOffsetMode = PixelOffsetMode.None

当此模式处于活动状态时,对应于图像(在 * 正常 * 图像采样中)的顶部和左侧边界的外部像素将绘制在由容器(目标位图或设备上下文)定义的矩形区域的边缘中间。
因此,由于源图像很小并且其像素被放大了很多,所以第一水平和垂直行的像素被明显地切成两半。
这可以使用其他PixelOffsetMode解决:

e.Graphics.PixelOffsetMode = PixelOffsetMode.Half

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

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文档时,在选择一个而不是另一个时,似乎有 * 速度 * 的含义。实际上,差异可以忽略不计。
C++ documentation about this matter(和一般的GDI+)更明确和精确,应该使用它而不是.Net。

如何操作

我们可以将小的源Bitmap绘制成一个新的更大的Bitmap,并将其分配给**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 = Image.FromStream(New MemoryStream(File.ReadAllBytes("[File Path]")), True, False)
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**是一个辅助方法,用于在容器中缩放Image:

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
h43kikqp

h43kikqp2#

我见过几次的解决方案是创建一个PictureBox的重写类,它具有InterpolationMode作为class属性。然后你需要做的就是在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

正如评论中所说的,对于最近邻模式,你需要将PixelOffsetMode设置为Half。老实说,我不明白为什么他们要费心暴露这一点,而不是在内部渲染过程中自动选择它。
大小可以通过设置控件的SizeMode属性来控制。将其设置为Zoom将使其自动居中和扩展,而不会在控件的设置大小中剪裁。

相关问题