Nano Hash - криптовалюты, майнинг, программирование

Рендеринг больших холстов в UserControl

У меня возникли проблемы с попыткой реализовать это в течение нескольких дней. Я много искал похожие вопросы в отношении того, что я пытаюсь сделать, но я не сталкивался с вопросом, который напрямую помогает моим проблемам.

По сути, я визуализирую плитки на сетку в своем классе 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);
        }
    }
}

Просто прокомментируйте, если вам нужно больше контекста из моего кода.

05.12.2012

Ответы:


1

Хитрость заключается в том, чтобы не вообще использовать для этого большое растровое изображение. Вам нужно только растровое изображение, покрывающее видимую область. Затем вы рисуете все, что видно.

Для этого вам нужно хранить данные отдельно от растрового изображения. Это может быть простой массив или массив/список с простым классом, содержащим информацию для каждого блока, такую ​​как позиция в мире.

Когда ваш блок находится в видимой области, вы рисуете его. Вы можете или не можете перебирать весь массив, но это не проблема (вы также можете вычислить видимый массив в отдельном потоке). Вы также можете сделать функцию более интеллектуальной, создав индексы регионов, чтобы не перебирать все блоки.

Чтобы добавить новый блок в массив, вычислите его положение холста в мировых координатах, добавьте его, а затем снова отобразите массив (или область, где нарисован блок).

Таким же образом система отрисовывает элементы управления с прокручиваемыми областями.

Включите двойную буферизацию, чтобы изображение оставалось четким и без мерцания.

В этом случае я бы также использовал панель с отдельными полосами прокрутки и вычислил относительное положение полос прокрутки.

06.12.2012
  • Абсолютно фантастично! Как долго я возился с этим, скорее всего, туннельное зрение заставило меня даже не думать об этом. Большое спасибо за четкое объяснение! 06.12.2012
  • Нет проблем, приятель. Я просто хотел добавить, что вам не нужно перерисовывать весь холст, если прокручивать с небольшим шагом. Просто переместите содержимое вниз/вверх и т. д. и перерисуйте только новый открытый пробел. 06.12.2012
  • Да, я пытался реализовать это с помощью метода прямого рисования, но при использовании этого метода это имеет гораздо больше смысла. 06.12.2012
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..