У меня возникли проблемы с попыткой реализовать это в течение нескольких дней. Я много искал похожие вопросы в отношении того, что я пытаюсь сделать, но я не сталкивался с вопросом, который напрямую помогает моим проблемам.
По сути, я визуализирую плитки на сетку в своем классе UserControl
. Это для моего редактора мира на основе Tile Engine, который я разрабатываю. Вот скриншот документа с открытым миром и некоторые плитки, обработанные кистью.
Изначально я собирался использовать Bitmap
в своем элементе управления, который будет холстом предварительного просмотра мира. Например, при использовании инструмента «Кисть», когда вы перемещаете мышь и нажимаете левую кнопку, он устанавливает ближайшую плитку под курсором в плитку кисти и рисует ее на растровом изображении layer
. Метод OnPaint
элемента управления переопределяется так, что растровое изображение layer
рисуется относительно прямоугольника отсечения события рисования.
Проблема с этим методом заключается в том, что при работе с большими мирами растровое изображение будет очень большим. Мне нужно, чтобы это приложение было универсальным с мировыми размерами, и совершенно очевидно, что при рендеринге больших растровых изображений в элементе управления каждый раз, когда он становится недействительным, возникают проблемы с производительностью.
В настоящее время я рисую плитки на элементе управления непосредственно в переопределенном событии OnPaint
моего элемента управления. Это здорово, потому что не требует много памяти. Например, мир (1000, 1000)
с (20, 20)
на плитку (общий размер холста (20000, 20000)
) занимает около 18 МБ памяти для всего приложения. Несмотря на то, что он не требует много памяти, он довольно интенсивно использует процессор, потому что каждый раз, когда элемент управления становится недействительным, он перебирает каждую плитку в окне просмотра. Это производит очень раздражающее мерцание.
Чего я хочу достичь, так это найти способ достичь середины в отношении использования памяти и производительности. По сути, дважды буферизируйте мир, чтобы он не мерцал при перерисовке элемента управления (изменение размера формы, фокусировка и размытие, прокрутка и т. д.). Возьмем, к примеру, 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);
}
}
}
Просто прокомментируйте, если вам нужно больше контекста из моего кода.