vb.net - 禁用图片框上的图像混合

标签 vb.net winforms bitmap gdi+ picturebox

在我的 Windows 窗体程序中,我有一个 PictureBox包含一个小图像,5 x 5像素。
当这个 Bitmap 被分配给 PictureBox.Image属性,它变得非常模糊。
我试图找到诸如混合模式、模糊模式或抗锯齿模式之类的东西,但我没有运气。
Image1 Image2

  This is what I want     This is not what I want

最佳答案

问题 :
位图的大小比用于显示它的容器小得多,它变得模糊,并且明确定义的颜色区域的其他锐利边缘被毫不客气地混合在一起。
这只是放大时将双线性过滤器应用于非常小的图像(几个像素)的结果。
期望的结果是在放大图像时保持单个像素的原始颜色。
为了达到这个效果,设置 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 文档,在选择一个而不是另一个时似乎会影响速度。差异实际上可以忽略不计。
C++ documentation about this matter (以及一般的 GDI+),它更加明确和精确,应该使用它而不是 .Net 。
如何进行 :
我们可以将小的源 Bitmap 绘制到一个新的更大的 Bitmap 并将其分配给 PictureBox.Image 属性(property)。
但是,假设 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

关于vb.net - 禁用图片框上的图像混合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54720916/

相关文章:

java - 为什么我的文字不显示?

vb.net - 为什么这个 ASCII 字符输出为空字符串 (VB.NET)?

vb.net - HttpClient 返回 401 以及正确的授权 header

mysql - 在 vb.net 中将 mysql 数据库表显示到 datagridview 中

c# - 使用 Javascript 折叠 RadFileExplorer

c# - BindingSource/DataGridView 交互

java - 加载照片时位图 OutOfMemoryException

.net - Windows 窗体应用程序中的富文本

c# - GeckoWebBrowser访问一个错误的URL,总是弹出消息框

android - 为什么 BitmapFactory.decodeResource 会缩放我的图像?