VB.NET如何消除缩放切片地图时PictureBox控件之间的空白间隙
火焰纹章7地图复刻缩放出现白边问题
出于兴趣我正在尝试复刻我最喜爱的游戏之一《火焰纹章7》的第一关。我从网上获取了一张地图图片,将其拆分为多个“方块”,每个方块对应一个PictureBox控件来展示对应切片内容,以此实现为每个方块附加地形值、驻留单位等属性的需求。
原地图尺寸仅为240×160,因此我需要支持将其缩放到用户自定义的任意尺寸。缩放系数为c时,每个方块的尺寸应为16c×16c(所有地图尺寸均可被16整除)。但实际运行时,当c>1,方块之间会出现白色线条,我核查过代码,理论上无论c取值多少,方块都应该完全相邻没有空隙。
下方是我的部分代码以及不同c值对应的运行效果截图:
'This Sub Creates The Map From Initial Image And Assigns Part Of Image to Each Square Public Sub New(Name As String, Image As Image) Dim cropRect As Rectangle Dim cropImage As Bitmap Me.Name = Name Me.Image = Image Height = Me.Image.Height / 16 Width = Me.Image.Width / 16 ReDim Squares(Height - 1, Width - 1) For i = 0 To Height - 1 For j = 0 To Width - 1 cropRect = New Rectangle(16 * j, 16 * i, 16, 16) cropImage = New Bitmap(16, 16) Graphics.FromImage(cropImage).DrawImage(Me.Image, 0, 0, cropRect, GraphicsUnit.Pixel) Squares(i, j) = New Square(cropImage) Next Next End Sub 'This Sub Sizes Each Square With User Defined Scale Value Public Sub Draw(Scale As Double) For i = 0 To Height - 1 For j = 0 To Width - 1 With Squares(i, j).Box .Size = New Size(16 * Scale, 16 * Scale) .Location = New Point(16 * j * Scale, 16 * i * Scale) .SizeMode = PictureBoxSizeMode.StretchImage End With Next Next End Sub
C=1时的地图图像
C=3时的地图图像
解决方案
白边问题由GDI+渲染特性、控件默认属性、浮点计算误差三类问题共同导致,按以下步骤修改即可解决:
- 关闭PictureBox默认边距和边框
默认情况下PictureBox自带边框、内边距和外边距,会占用额外空间导致方块之间出现空隙。在Square类初始化PictureBox时添加以下配置:
With Squares(i, j).Box .BorderStyle = BorderStyle.None .Margin = Padding.Empty .Padding = Padding.Empty End With
- 统一整数计算避免浮点截断误差
当前计算尺寸和坐标时直接使用Double类型,赋值给需要Integer参数的Size和Point时会发生隐式截断,多次计算后误差累积就会出现空隙。修改Draw方法提前计算整数的缩放单位:
Public Sub Draw(Scale As Double) ' 提前将单格尺寸转为整数,避免浮点误差累积 Dim scaledUnit As Integer = CInt(16 * Scale) For i = 0 To Height - 1 For j = 0 To Width - 1 With Squares(i, j).Box .Size = New Size(scaledUnit, scaledUnit) .Location = New Point(j * scaledUnit, i * scaledUnit) .SizeMode = PictureBoxSizeMode.StretchImage End With Next Next End Sub
- 调整切片绘制时的渲染参数
GDI+默认的插值模式会在图像边缘生成半透明过渡像素,拼接时就会显示为白色缝隙。修改切片时的DrawImage逻辑,适配像素风游戏的渲染需求:
cropRect = New Rectangle(16 * j, 16 * i, 16, 16) cropImage = New Bitmap(16, 16) Using g As Graphics = Graphics.FromImage(cropImage) ' 使用最近邻插值避免边缘模糊和半透明像素 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half g.DrawImage(Me.Image, 0, 0, cropRect, GraphicsUnit.Pixel) End Using
如果追求更高的运行性能,建议放弃多PictureBox的方案,直接在单个自定义控件的Paint事件中批量绘制所有地图方块,避免大量控件带来的内存和渲染开销。
内容的提问来源于stack exchange,提问作者TheBowlOfCereal
相关产品推荐
相关产品推荐

