几天来,我一直在尝试实施此操作时遇到麻烦。关于我正在尝试做的事情,我已经广泛搜索了类似的问题,但我没有遇到直接帮助我解决问题的问题。
基本上,我在UserControl
课堂上将瓷砖渲染到网格上。这是我正在开发的基于 Tile Engine 的世界编辑器。这是一个开放世界文档的屏幕截图和一些刷过的图块。
最初,我打算Bitmap
在我的控件中使用一个作为世界预览画布的控件。例如,使用画笔工具时,当您移动鼠标并按住左键时,它会将光标下方最近的图块设置为画笔的图块,并将其绘制在位layer
图上。控件的OnPaint
方法被覆盖到layer
相对于绘制事件的剪切矩形绘制位图的位置。
这种方法的问题是,在处理大世界时,位图会非常大。我需要这个应用程序在世界大小上具有通用性,而且很明显,每次将大型位图渲染到控件上时都会出现性能问题。
目前,我在控件的覆盖OnPaint
事件中直接将图块绘制到控件上。这很棒,因为它不需要大量内存。例如,每个 tile 的(1000, 1000)
世界(20, 20)
(总画布大小为(20000, 20000)
)在整个应用程序的大约 18mb 内存中运行。虽然不是内存密集型,但它是相当密集的处理器,因为每次控件无效时,它都会遍历视口中的每个图块。这会产生非常烦人的闪烁。
我想要完成的是一种在内存使用和性能方面处于中间状态的方法。本质上是双重缓冲世界,以便在重绘控件时不会闪烁(表单调整大小、焦点和模糊、滚动等)。以 Photoshop 为例——当它溢出容器视口时,它如何渲染打开的文档?
OnPaint
作为参考,这是我使用上述直接绘制方法的控件的覆盖。
getRenderBounds
返回一个相对于PaintEventArgs.ClipRectangle
用于渲染可见图块的矩形,而不是循环遍历世界中的所有图块并检查它是否可见。
protected override void OnPaint(PaintEventArgs e)
{
WorldSettings settings = worldSettings();
Rectangle bounds = getRenderBounds(e.ClipRectangle),
drawLocation = new Rectangle(Point.Empty, settings.TileSize);
e.Graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
e.Graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.None;
e.Graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.None;
e.Graphics.CompositingQuality =
System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
for (int x = bounds.X; x < bounds.Width; x++)
{
for (int y = bounds.Y; y < bounds.Height; y++)
{
if (!inWorld(x, y))
continue;
Tile tile = getTile(x, y);
if (tile == null)
continue;
drawLocation.X = x * settings.TileSize.Width;
drawLocation.Y = y * settings.TileSize.Height;
e.Graphics.DrawImage(img,
drawLocation,
tileRectangle,
GraphicsUnit.Pixel);
}
}
}
如果您需要我的代码中的更多上下文,请发表评论。