PixelOffsetMode.Default和PixelOffsetMode.HighSpeed与PixelOffsetMode.None相同。 PixelOffsetMode.HighQuality与PixelOffsetMode.Half相同。 阅读.Net文档时,在选择一个而不是另一个时,似乎有 * 速度 * 的含义。实际上,差异可以忽略不计。 C++ documentation about this matter(和一般的GDI+)更明确和精确,应该使用它而不是.Net。
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
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
2条答案
按热度按时间ndasle7k1#
问题:
一个位图,如果它的大小比用来显示它的容器小得多,那么它就会变得模糊不清,而且定义明确的颜色区域的尖锐边缘也会被随意地混合。
这只是放大时应用于非常小的图像(几个像素)的双线性滤镜的结果。
所需的结果是在放大图像时保持单个像素的原始颜色。
要实现此结果,只需将Graphics对象的InterpolationMode设置为:
此过滤器也称为**
Point Filter
**,它只是选择一种与正在评估的像素颜色最接近的颜色。当评估颜色均匀的区域时,结果是所有像素的像素颜色相同。只有一个问题,即Graphics对象的默认值PixelOffsetMode,即:
当此模式处于活动状态时,对应于图像(在 * 正常 * 图像采样中)的顶部和左侧边界的外部像素将绘制在由容器(目标位图或设备上下文)定义的矩形区域的边缘中间。
因此,由于源图像很小并且其像素被放大了很多,所以第一水平和垂直行的像素被明显地切成两半。
这可以使用其他
PixelOffsetMode
解决:此模式将图像的渲染位置向后移动半个像素。
结果的示例图像可以更好地解释这一点:
备注:
.Net的MSDN文档没有很好地描述
PixelOffsetMode
参数。您可以找到6个明显不同的选项。像素偏移模式实际上只有两个:PixelOffsetMode.None
(默认)和**PixelOffsetMode.Half
**。PixelOffsetMode.Default
和PixelOffsetMode.HighSpeed
与PixelOffsetMode.None
相同。PixelOffsetMode.HighQuality
与PixelOffsetMode.Half
相同。阅读.Net文档时,在选择一个而不是另一个时,似乎有 * 速度 * 的含义。实际上,差异可以忽略不计。
C++ documentation about this matter(和一般的GDI+)更明确和精确,应该使用它而不是.Net。
如何操作:
我们可以将小的源Bitmap绘制成一个新的更大的Bitmap,并将其分配给**
PictureBox.Image
**属性。但是,假设PictureBox的大小在某个时候发生了变化(因为布局发生了变化和/或因为DPI Awareness妥协),我们(几乎)回到了起点。
一个简单的解决方案是直接在控件的表面绘制新的位图,并在必要时将其保存到光盘。
这也将允许在需要时缩放位图,而不会损失质量:
**
GetScaledImageRect
**是一个辅助方法,用于在容器中缩放Image:h43kikqp2#
我见过几次的解决方案是创建一个
PictureBox
的重写类,它具有InterpolationMode
作为class属性。然后你需要做的就是在UI上使用这个类而不是.Net自己的PictureBox
,并将该模式设置为NearestNeighbor
。正如评论中所说的,对于最近邻模式,你需要将
PixelOffsetMode
设置为Half
。老实说,我不明白为什么他们要费心暴露这一点,而不是在内部渲染过程中自动选择它。大小可以通过设置控件的
SizeMode
属性来控制。将其设置为Zoom
将使其自动居中和扩展,而不会在控件的设置大小中剪裁。