#java #java-2d
Вопрос:
У меня проблемы с производительностью с Java2D. Я знаю параметр виртуальной машины sun.java2d.opengl для включения 3D-ускорения для 2D, но даже при его использовании возникают некоторые странные проблемы.
Вот результаты тестов, которые я провел:
Рисование карты 25×18 с плитками размером 32×32 пикселя на jкомпонентном
изображении 1 = формат bmp, изображение 2 = формат. png
Без -Dsun.java2d.opengl=true
Использование 120 кадров в секунду .Изображение BMP 1
13 кадров в секунду с использованием .PNG изображение 2
С помощью-Dsun.java2d.opengl=true
Использование 12 кадров в секунду .Использование BMP-изображения 1
700 кадров в секунду .PNG изображение 2
Без ускорения я предполагаю, что с каждым рисованным изображением происходит какая-то трансформация() Я делаю это в программном обеспечении и значительно снижаю FPS в случае .PNG. Однако почему при ускорении результаты будут меняться (а PNG на самом деле работает невероятно быстрее)?! Сумасшествие!
.BMP Изображение 1 преобразуется в тип изображения TYPE_INT_RGB. .PNG Изображение 2 преобразуется в тип изображения TYPE_CUSTOM. Чтобы получить согласованную скорость с ускорением opengl и без него, я должен создать новое буферизованное изображение с типом изображения TYPE_INT_ARGB и нарисовать изображение 1 или изображение 2 на этом новом изображении.
Вот результаты, полученные с помощью этого:
Без -Dsun.java2d.opengl=true
Использование 120 кадров в секунду .Изображение BMP 1
120 кадров в секунду с использованием .PNG изображение 2
С помощью-Dsun.java2d.opengl=true
Использование 700 кадров в секунду .Использование BMP-изображения 1
700 кадров в секунду .PNG изображение 2
Мой реальный вопрос заключается в том, могу ли я предположить, что TYPE_INT_ARGB будет типом собственного образа для всех систем и платформ? Я предполагаю, что это значение может быть другим. Есть ли какой-то способ получить собственное значение, чтобы я всегда мог создавать новые буферные изображения для максимальной производительности?
Заранее спасибо…
Ответ №1:
Я думаю, что нашел решение, исследовав и собрав воедино кусочки и фрагменты из слишком большого количества поисковых запросов Google.
Вот оно, комментарии и все:
private BufferedImage toCompatibleImage(BufferedImage image)
{
// obtain the current system graphical settings
GraphicsConfiguration gfxConfig = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
/*
* if image is already compatible and optimized for current system
* settings, simply return it
*/
if (image.getColorModel().equals(gfxConfig.getColorModel()))
return image;
// image is not optimized, so create a new image that is
BufferedImage newImage = gfxConfig.createCompatibleImage(
image.getWidth(), image.getHeight(), image.getTransparency());
// get the graphics context of the new image to draw the old image on
Graphics2D g2d = newImage.createGraphics();
// actually draw the image and dispose of context no longer needed
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
// return the new optimized image
return newImage;
}
В моем предыдущем посте GraphicsConfiguration содержала информацию, необходимую для создания оптимизированных изображений в системе. Кажется, это работает довольно хорошо, но я бы подумал, что Java автоматически сделает это за вас. Очевидно, что вы не можете чувствовать себя слишком комфортно с Java. 🙂 Думаю, в итоге я ответил на свой собственный вопрос. Ну что ж, надеюсь, это поможет некоторым из вас, которых я видел, когда они пытались использовать Java для 2D-игр.
Комментарии:
1. Это просто потрясающе. Это заставляет мой код работать намного быстрее. Большое спасибо.
2. Я приношу извинения за большую задержку с тем, чтобы отметить это как ответ.
3. БОЛЬШОЕ ВАМ СПАСИБО!
Ответ №2:
Ну, это старый пост, но я хотел бы поделиться своими выводами о прямом рисовании с помощью Swing/AWT, без буферного изображения.
Некоторые виды рисунка, такие как 3D, лучше выполнять при рисовании непосредственно в буфер int []. После создания изображений вы можете использовать экземпляр ImageProducer, например MemoryImageSource, для создания изображений. Я предполагаю, что вы знаете, как выполнять свои рисунки напрямую, без помощи графики/Графики2.
/**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {
public int pixel[];
public int width;
public int height;
private Image imageBuffer;
private MemoryImageSource mImageProducer;
private ColorModel cm;
private Thread thread;
public MyCanvas() {
super(true);
thread = new Thread(this, "MyCanvas Thread");
}
/**
* Call it after been visible and after resizes.
*/
public void init(){
cm = getCompatibleColorModel();
width = getWidth();
height = getHeight();
int screenSize = width * height;
if(pixel == null || pixel.length < screenSize){
pixel = new int[screenSize];
}
mImageProducer = new MemoryImageSource(width, height, cm, pixel,0, width);
mImageProducer.setAnimated(true);
mImageProducer.setFullBufferUpdates(true);
imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);
if(thread.isInterrupted() || !thread.isAlive()){
thread.start();
}
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
// rubisch draw
int[] p = pixel; // this avoid crash when resizing
if(p.length != width * height) return;
for(int x=0; x < width; x ){
for(int y=0; y<height; y ){
int color = (((x i) % 255) amp; 0xFF) << 16; //red
color |= (((y j) % 255) amp; 0xFF) << 8; //green
color |= (((y/2 x/2 - j) % 255) amp; 0xFF) ; //blue
p[ x y * width] = color;
}
}
i = 1;
j = 1;
}
private int i=1,j=256;
@Override
public void run() {
while (true) {
// request a JPanel re-drawing
repaint();
try {Thread.sleep(5);} catch (InterruptedException e) {}
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// perform draws on pixels
render();
// ask ImageProducer to update image
mImageProducer.newPixels();
// draw it on panel
g.drawImage(this.imageBuffer, 0, 0, this);
}
/**
* Overrides ImageObserver.imageUpdate.
* Always return true, assuming that imageBuffer is ready to go when called
*/
@Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
return true;
}
}// end class
Примечание. Нам нужен уникальный экземпляр MemoryImageSource и изображения. Не создавайте новое изображение или новый создатель изображений для каждого кадра, если вы не изменили размер своей JPanel. См. Метод init() выше.
В потоке рендеринга попросите перерисовать(). На Swing функция repaint() вызовет переопределенный компонент PAINT (), где он вызовет ваш метод render (), а затем попросит вашего производителя изображений обновить изображение. Когда изображение готово, нарисуйте его с помощью Graphics.drawImage().
Чтобы иметь совместимое изображение, используйте правильную цветовую модель при создании изображения. Я использую GraphicsConfiguration.getColorModel():
/**
* Get Best Color model available for current screen.
* @return color model
*/
protected static ColorModel getCompatibleColorModel(){
GraphicsConfiguration gfx_config = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration();
return gfx_config.getColorModel();
}
Комментарии:
1. Просто интересно, какие люди отклоняют правильные ответы… Это (i) о Java2D; (ii) о высокой производительности при рисовании изображений; (iii) о рендеринге изображений, поступающих из других источников (OpenGL, собственный рендерер, программное обеспечение для визуализации и т. Д.). Так ЗАЧЕМ же понижать голос???
Ответ №3:
Из того, что я помню, когда я думал о графическом программировании на Java, встроенные библиотеки работают медленно. Мне посоветовали GameDev.Net что любой, кто делает что-то серьезное, должен будет использовать что-то вроде jogl
Комментарии:
1. Ну, так как самая большая потеря производительности на самом деле заключается в вызове drawImage (), я бы счел 450 вызовов с частотой 700 кадров в секунду довольно хорошими. Мне пришлось бы написать то же самое в jogl, чтобы увидеть, есть ли существенная разница, но в последних версиях Java с Java2D был достигнут большой прогресс.