在Windows窗体程序中,我有一个PictureBox
。其中的位图图像很小,5 x 5
像素。
当分配给PictureBox
时,它变得非常模糊。
我尝试查找诸如混合模式,模糊模式或抗锯齿模式之类的东西,但是我没有运气。
This is what I want This is not what I want
答案 0 :(得分:5)
问题:
模糊了一个位图,该位图的大小比用于显示它的容器小得多,并且本来可以很好地混合颜色定义明确的区域的尖锐边缘。
这只是将双线性滤镜放大后应用于很小的图像(几个像素)的结果。
所需的结果是在放大图像时保持单个像素的原始颜色。
要获得此结果,只需将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.Default
和PixelOffsetMode.HighSpeed
与PixelOffsetMode.None
相同。
PixelOffsetMode.HighQuality
与PixelOffsetMode.Half
相同。
阅读.Net Docs时,选择其中一项似乎有 speed 含义。差异实际上可以忽略不计。
C++ documentation about this matter(通常是GDI +)更加明确和精确,应该使用它代替.Net。
如何进行:
我们可以将较小的源位图绘制到一个新的较大的位图,并将其分配给 PictureBox.Image
属性。
但是,假设PictureBox的大小在某个时候发生了变化(因为布局更改和/或由于DPI意识的折衷),我们(几乎)回到了平方。
一个简单的解决方案是直接在控件的表面上绘制新的位图,并在必要时将其保存到光盘中。
这还将允许在需要时缩放位图,而不会损失质量:
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
如注释中所述,对于最近邻居模式,您需要设置
PixelOffsetMode
至Half
。老实说,我不明白为什么他们不愿在内部渲染过程中将其公开而不是自动选择。
可以通过设置控件的SizeMode
属性来控制大小。将其放在Zoom
上将使其自动居中并展开,而不会削减控件的设置大小。