Подтвердить что ты не робот

Графика Java bufferstrategy или целочисленный массив

При разработке 2D-игр в Java большинство учебников создают буферную стратегию для рендеринга. Это имеет смысл. Однако, когда люди, похоже, перекошены, это метод рисования фактической графики в буфер.

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

private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

Graphics g = bs.getDrawGraphics();
g.setColor(new Color(0x556B2F));
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);

Однако некоторые другие учебники не создают буферизованное изображение, рисуют пиксели в массив int и вместо этого используют компонент Graphics из BufferStrategy для рисования их изображений непосредственно в буфер.

Graphics g = bs.getDrawGraphics();
g.setColor(new Color(0x556B2F));
g.fillRect(0, 0, getWidth(), getHeight());

g.drawImage(testImage.image, x*128, y*128, 128, 128, null);

Мне просто интересно, зачем создавать весь массив int, а затем рисовать его. Для этого требуется гораздо больше работы по реализации прямоугольников, растягиванию, прозрачности и т.д. Графический компонент стратегии буфера уже имеет методы, которые можно легко вызвать. Есть ли огромное увеличение производительности использования массива int?

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

4b9b3361

Ответ 1

Давайте поговорим об одном: оба фрагмента кода делают то же самое - рисуем изображение. Однако фрагменты довольно неполные - второй фрагмент не показывает, что такое "testImage.image" на самом деле или как оно создается. Но они оба в конечном итоге называют Graphics.drawImage() и все варианты drawImage() в Graphics или Graphics2D, рисуют изображение простым и простым. Во втором случае мы просто не знаем, является ли это BufferedImage, VolatileImage или даже Toolkit Image.

Таким образом, нет никакой разницы в рисовании, проиллюстрированном здесь!

Существует только одно различие между двумя фрагментами - первая и получает прямую ссылку на целочисленный массив, который в конечном счете внутренне поддерживает экземпляр Image. Это дает прямой доступ к данным пикселя, а не для прохождения через (Буферизованный) API изображений для использования, например, относительно медленных методов getRGB() и setRGB(). Причина, почему это сделать не может быть конкретным в контексте, заключается в этом вопросе, массив получается, но никогда не используется в фрагменте. Поэтому для того, чтобы дать следующее объяснение любой причине существования, мы должны сделать предположение, что кто-то хочет прямо читать или редактировать пиксели изображения, вполне возможно, по соображениям оптимизации, учитывая "медленность" (Buffered) Image API для манипулировать данными.

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


В общем, этот код работает только потому, что тип изображения - INT_RGB, который даст изображение IntDataBuffer. Если это был другой тип изображения, ex 3BYTE_BGR, этот код будет терпеть неудачу с ClassCastException, поскольку буфер данных резервного копирования не будет IntDataBuffer. Это может быть не очень проблематично, когда вы только вручную создаете изображения и применяете определенный тип, но изображения, как правило, загружаются из файлов, созданных внешними инструментами.

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

Как создать изображение с аппаратным ускорением с помощью Java2D?

(Поскольку этот связанный вопрос показывает вам: вы должны использовать GraphicsConfiguration.createCompatibleImage() для создания экземпляров BufferedImage).

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

http://www.pushing-pixels.org/2008/06/06/effective-java2d.html

Ответ 2

Прежде всего, есть много исторических аспектов. Ранний API был очень простым, поэтому единственным способом сделать что-то нетривиальным было внедрение всех необходимых примитивов.

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

1. Эффекты фильтра

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

enter image description here

Простым способом реализации такого эффекта с Java 1 было использование массива int и фильтра, определенного как матрица. Например, у Герберта Шильдта было много таких демонстраций:

public class Blur {

    public void convolve() {
        for (int y = 1; y < height - 1; y++) {
            for (int x = 1; x < width - 1; x++) {
                int rs = 0;
                int gs = 0;
                int bs = 0;
                for (int k = -1; k <= 1; k++) {
                    for (int j = -1; j <= 1; j++) {
                        int rgb = imgpixels[(y + k) * width + x + j];
                        int r = (rgb >> 16) & 0xff;
                        int g = (rgb >> 8) & 0xff;
                        int b = rgb & 0xff;
                        rs += r;
                        gs += g;
                        bs += b;
                    }
                }
                rs /= 9;
                gs /= 9;
                bs /= 9;
                newimgpixels[y * width + x] = (0xff000000
                        | rs << 16 | gs << 8 | bs);
            }
        }
    }
} 

Естественно, вы можете реализовать это с помощью getRGB, но исходный доступ к данным более эффективен. Позже Graphics2D обеспечил лучший уровень абстракции:

публичный интерфейс BufferedImageOp

Этот интерфейс описывает операции с одним входом/с одним выходом, выполняемые на BufferedImage объекты. Он реализуется AffineTransformOp, ConvolveOp, ColorConvertOp, RescaleOp и LookupOp. Эти объекты могут быть переданы в BufferedImageFilter для работы с BufferedImage в ImageProducer-ImageFilter-ImageConsumer.

2. Двойная буферизация

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

enter image description here

Что-то вроде окончательного вывода:)

Я бы сказал, что ситуация, о которой вы описали, довольно распространена для любой развивающейся технологии. Существует два способа достижения одинаковых целей:

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

Есть также некоторые полезные расширения, чтобы упростить вашу жизнь еще больше, поэтому не нужно использовать int[]:)