禁用PictureBox上的图像混合

问题描述 投票:3回答:2

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

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

   This is what I want     This is not what I want
vb.net winforms bitmap picturebox color-blending
2个回答
6
投票

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

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

要实现此结果,将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种,显然不同的选择。 Pixel Offset模式实际上只有两个: PixelOffsetMode.None(默认)和PixelOffsetMode.Half

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

C++ documentation about this matter(和一般的GDI +)更加明确和精确,它应该被用来代替.Net。

如何进行:

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

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

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

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

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是一个帮助方法,用于在容器内缩放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

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

正如评论中所述,对于最近邻模式,您需要将PixelOffsetMode设置为Half。老实说,我不明白为什么他们不愿意在内部渲染过程中将其暴露出来而不是自动选择。

可以通过设置控件的SizeMode属性来控制大小。将它放到Zoom将使它自动居中和扩展而不会在控件的设置大小中进行裁剪。

© www.soinside.com 2019 - 2024. All rights reserved.