Графика 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?
Я искал это часами, и все сайты, которые я видел, просто объясняют, что они делают, а не почему они решили сделать это таким образом.
Ответы
Ответ 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[]
:)