禁用图片框上的图像混合 [英] Disable Image blending on a PictureBox
问题描述
在我的 Windows 窗体程序中,我有一个 PictureBox
,其中包含一个小图像,5 x 5
像素.
当将此位图分配给 PictureBox.Image
属性时,它会变得非常模糊.
我试图找到诸如混合模式、模糊模式或抗锯齿模式之类的东西,但我没有运气.
这就是我想要的 这不是我想要的
问题:
位图的大小比用于显示它的容器小得多,被模糊,并且明确定义的颜色区域的其他锐边被毫不客气地混合在一起.
这只是在放大时将双线性过滤器应用于非常小的图像(几个像素)的结果.
期望的结果是在放大图像时保持单个像素的原始颜色.
要达到这个效果,设置Graphics对象的
默认过滤器 InterpolationMode InterpolationModeInterpolationMode NearestNeighbor NearestNeighbor双线性 PixelOffsetMode.None PixelOffsetMode.Half
注意:
.Net 的 MSDN 文档没有很好地描述 PixelOffsetMode
参数.您可以找到 6 个明显不同的选择.Pixel Offset 模式实际上只有两种:PixelOffsetMode.None
(默认)和 PixelOffsetMode.Half
.
PixelOffsetMode.Default
和 PixelOffsetMode.HighSpeed
与 PixelOffsetMode.None
相同.PixelOffsetMode.HighQuality
与 PixelOffsetMode.Half
相同.
阅读 .Net 文档,在选择其中一个时似乎有速度影响.差异实际上可以忽略不计.
导入 System.Drawing导入 System.Drawing.Drawing2DPrivate pixelBitmap As Bitmap = NothingPrivate Sub Form1_Load(sender As Object, e As EventArgs) 处理 MyBase.LoadpixelBitmap = DirectCast(New Bitmap("File Path").Clone(), Bitmap)结束子Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) 处理PictureBox1.Painte.Graphics.InterpolationMode = InterpolationMode.NearestNeighbore.Graphics.PixelOffsetMode = PixelOffsetMode.Halfe.Graphics.DrawImage(pixelBitmap, GetScaledImageRect(pixelBitmap, DirectCast(sender, Control)))结束子Private Sub PictureBox1_Resize(sender As Object, e As EventArgs) 处理PictureBox1.ResizePictureBox1.Invalidate()结束子
GetScaledImageRect
是一个辅助方法,用于在容器内缩放图像:
公共函数GetScaledImageRect(image As Image, canvas As Control) As RectangleF返回 GetScaledImageRect(image, canvas.ClientSize)结束函数公共函数 GetScaledImageRect(image As Image, containerSize As SizeF) As RectangleF将 imgRect 调暗为 RectangleF = RectangleF.EmptyDim scaleFactor As Single = CSng(image.Width/image.Height)Dim containerRatio As Single = containerSize.Width/containerSize.Height如果 containerRatio >= scaleFactor 那么imgRect.Size = New SizeF(containerSize.Height * scaleFactor, containerSize.Height)imgRect.Location = New PointF((containerSize.Width - imgRect.Width)/2, 0)别的imgRect.Size = New SizeF(containerSize.Width, containerSize.Width/scaleFactor)imgRect.Location = New PointF(0, (containerSize.Height - imgRect.Height)/2)万一返回 imgRect结束函数
In my Windows Forms program, I have a PictureBox
that contains a small image, 5 x 5
pixels.
When this Bitmap is assigned to the PictureBox.Image
property, it becomes very blurry.
I tried to find something like blending mode, blurring mode, or anti-aliasing mode, but I had no luck.
This is what I want This is not what I want
The problem:
A Bitmap, with a size that is much smaller than the container used to show it, is blurred and the otherwise sharp edges of the well-defined areas of color are unceremoniously blended.
This is just the result of a Bilinear filter applied to a really small image (a few pixels) when zoomed in.
The desired result is to instead maintain the original color of the single pixels while the Image is enlarged.
To achieve this result, it's enough to set the Graphics object's InterpolationMode to:
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
This filter, also known as Point Filter
, simply selects a color which is the nearest to the pixel color that is being evaluated. When evaluating homogeneous areas of color, the result is the same pixel color for all the pixels.
There's just one problem, the default value of the Graphics object's PixelOffsetMode, which is:
e.Graphics.PixelOffsetMode = PixelOffsetMode.None
With this mode active, the outer pixels, corresponding to the top and left borders of an Image (in the normal image sampling) are drawn in the middle of the edges of the rectangular area defined by the container (the destination Bitmap or device context).
Because of this, since the source Image is small and its pixels are enlarged quite a lot, the pixels of the first horizontal and vertical lines are visibly cut in half.
This can be resolved using the other PixelOffsetMode
:
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half
This mode moves back the image's rendering position by half a pixel.
A sample image of the results can explain this better:
Default Filter InterpolationMode InterpolationMode
InterpolationMode NearestNeighbor NearestNeighbor
Bilinear PixelOffsetMode.None PixelOffsetMode.Half
Note:
The .Net's MSDN Docs do not describe the PixelOffsetMode
parameter very well. You can find 6, apparently different, choices. The Pixel Offset modes are actually only two:
PixelOffsetMode.None
(the default) and PixelOffsetMode.Half
.
PixelOffsetMode.Default
and PixelOffsetMode.HighSpeed
are the same as PixelOffsetMode.None
.
PixelOffsetMode.HighQuality
is the same as PixelOffsetMode.Half
.
Reading the .Net Docs, there seems to be speed implications when choosing one over the other. The difference is actually negligible.
The C++ documentation about this matter (and GDI+ in general), is much more explicit and precise, it should be used instead of the .Net one.
How to proceed:
We could draw the small source Bitmap to a new, larger Bitmap and assign it to a PictureBox.Image
property.
But, assume that the PictureBox size changes at some point (because the layout changes and/or because of DPI Awareness compromises), we're (almost) back at square one.
A simple solution is to draw the new Bitmap directly on the surface of a control and save it to disc when/if necessary.
This will also allow to scale the Bitmap when needed without losing quality:
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
is a helper method used to scale an Image inside a container:
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
这篇关于禁用图片框上的图像混合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!