Клонирование 60FPS игры на четыре экрана - PullRequest
2 голосов
/ 23 октября 2019

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

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

Ограничение на применение фильтра:

  • Фильтр будет работать только на изображении.
  • Фильтр написан на Java, который практически невозможно переписать.

Поэтому я применяю логику примерно так:

import java.awt.*;    
import javax.swing.ImageIcon;
import javax.swing.JFrame;    
import com.sun.jna.platform.win32.GDI32Util;
import com.sun.jna.platform.win32.WinDef.HWND;

import java.awt.image.BufferedImage;

public class MultiFrameApplet implements Runnable
{     
    public JFrame currentFrame = null;
    public PanelPaint currentCanvas = null; 
    public BufferedImage screenshotImage = null;
    com.sun.jna.platform.win32.User32 user32 = null;
    HWND hwnd = null;
    Thread th = null;
    String CurrentFrameText = "";
    private long lastTime;
    private double fps; //could be int or long for integer values
    public MultiFrameApplet(int filtertype)
    { 
        main(null,filtertype);      
    }     

    public void main(String[] argv,int filtertype) 
    {   
        try
        {
           GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
           GraphicsDevice[] gd = ge.getScreenDevices();

           //First Screen
           GraphicsConfiguration gcFirst = gd[0].getDefaultConfiguration();
           Toolkit toolkit = Toolkit.getDefaultToolkit();
           if (toolkit == null) 
           {
               return;
           }       
           Rectangle screenRectFirst = gcFirst.getBounds();
           Insets screenInsetsFirst = toolkit.getScreenInsets(gcFirst);
           screenRectFirst.x = screenInsetsFirst.left;
           screenRectFirst.y = screenInsetsFirst.top;

           Robot robot = new Robot(gcFirst.getDevice());

           //Second Screen
           GraphicsConfiguration gcSecond = gd[1].getDefaultConfiguration();               
           Rectangle screenRectSecond = gcSecond.getBounds();
           Insets screenInsetsSecond = Toolkit.getDefaultToolkit().getScreenInsets(gcSecond);       
           Rectangle effectiveScreenArea = new Rectangle();

           /*Remove start bar area*/
           effectiveScreenArea.x = screenRectSecond.x + screenInsetsSecond.left;
           effectiveScreenArea.y = screenRectSecond.y + screenInsetsSecond.top;
           effectiveScreenArea.height = screenRectSecond.height - screenInsetsSecond.top - screenInsetsSecond.bottom;        
           effectiveScreenArea.width = screenRectSecond.width - screenInsetsSecond.left - screenInsetsSecond.right;

           //Scaling will decide capture image needs to shrink or not.!
           double xscaling = 0;
           double yscaling = 0;

           screenshotImage = robot.createScreenCapture(screenRectFirst);

           int differenceWidth = screenRectSecond.width / screenRectFirst.width;
           int differenceheight = screenRectSecond.height / screenRectFirst.height;
           xscaling = differenceWidth / 2.0;
           yscaling = differenceheight / 2.0;   

            yscaling = yscaling - 0.018;

           currentFrame =  new JFrame(); 

           currentFrame.setSize((int)effectiveScreenArea.width/2, (int)effectiveScreenArea.height/2); 
           if(filtertype == 0)
           {
               currentFrame.setLocation(effectiveScreenArea.x, effectiveScreenArea.y); 
               currentFrame.setTitle("First");
               CurrentFrameText = "First";
           }
           else if(filtertype == 1)
           {
               currentFrame.setLocation(effectiveScreenArea.x + ((int)effectiveScreenArea.width/2), effectiveScreenArea.y); 
               currentFrame.setTitle("Second");
               CurrentFrameText = "Second";
           }
           else if(filtertype == 2)
           {
               currentFrame.setLocation(effectiveScreenArea.x + ((int)effectiveScreenArea.width/2), effectiveScreenArea.y); 
               currentFrame.setTitle("Third");
               CurrentFrameText = "Third";
           } 
           else if(filtertype == 3)
           {
               currentFrame.setLocation(effectiveScreenArea.x + ((int)effectiveScreenArea.width/2),effectiveScreenArea.y + ((int)effectiveScreenArea.height/2)); 
               currentFrame.setTitle("Forth");
               CurrentFrameText = "Forth";
           } 


           currentCanvas = new PanelPaint((int)effectiveScreenArea.width/2,(int)effectiveScreenArea.height/2,xscaling,yscaling,CurrentFrameText); 
           currentCanvas.xpos = (int)effectiveScreenArea.width/2;
           currentCanvas.ypos = (int)effectiveScreenArea.height/2;
           currentFrame.getContentPane().add(currentCanvas); 


           currentFrame.setUndecorated(true);        
           currentFrame.setVisible(true);       

            user32 = com.sun.jna.platform.win32.User32.INSTANCE;
           hwnd = user32.GetDesktopWindow();

           th = new Thread(this);
           th.start();     

        }
        catch (AWTException e) 
        {
           // TODO Auto-generated catch block
           e.printStackTrace();
        }
        catch (Exception e) 
        { 
            // TODO Auto-generated catch block
             e.printStackTrace();

        }
    }       
    public void close()
    { 
        currentFrame.dispose();
        currentFrame = null;
        currentCanvas.close();
        currentCanvas = null;   
        screenshotImage = null;
        user32 = null;
        hwnd = null;
        th = null;
    }

    @Override
    public void run() 
    {
        while(true)
        {
            lastTime = System.nanoTime();
            screenshotImage  = GDI32Util.getScreenshot(hwnd);       
            ///screenshotImage = screenshotImage.Convert();   //Place where filter applied
            currentCanvas.setImg(screenshotImage,fps);
            java.awt.EventQueue.invokeLater(currentCanvas::repaint); 
            fps = 1000000000.0 / (System.nanoTime() - lastTime); //one second(nano) divided by amount of time it takes for one frame to finish
            lastTime = System.nanoTime();
        }
    }

} 

@SuppressWarnings("serial")
class PanelPaint extends javax.swing.JPanel
{
    public int xpos = 0;
    public int ypos = 0;
    BufferedImage img = null;
    java.awt.Graphics2D gc  = null;
    Font currentFont = null;
    double fps = 0;
    String CurrentFrameText;
    PanelPaint(int xpos,int ypos,double sx,double sy,String argCurrentFrameText)
    {
        img = new BufferedImage(xpos, ypos, BufferedImage.TYPE_INT_ARGB);
        gc = img.createGraphics();
        gc.scale(sx, sy);
        currentFont = new Font("default", Font.BOLD, 30);
        CurrentFrameText =  argCurrentFrameText;    
        gc.setFont(currentFont);
        gc.setColor(Color.RED);

    }
    @Override
    public Dimension getPreferredSize()
    {
        return new Dimension(xpos, ypos);
    }

    public void setImg(BufferedImage img,double argfps)
    {
        gc.drawImage(img, 0, 0, null);
        gc.drawString(CurrentFrameText + ": " + (int)fps, 25, 25);
        fps = argfps;
    }

    @Override
    public void paint(java.awt.Graphics g)
    {
        g.drawImage(img, 0, 0, null);   
    }
    public void close()
    { 
        img = null;
        gc  = null;
        currentFont = null;
    }
}

Выше медленнее для высококонтрастных изображений и требуется время для " g.drawImage (img, 0,0, ноль);"код. Можно ли улучшить производительность рисования изображения?

1 Ответ

0 голосов
/ 05 ноября 2019

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

Это, безусловно, оптимальное решениек вашему требованию. Если ваша игра основана на OpenGL, она будет еще быстрее, поскольку вам не придется использовать пакет Robot в качестве промежуточного звена - вы можете просто визуализировать игровую текстуру напрямую (преобразование изображения не требуется).

Это также позволитполный контроль над фильтром во фрагментном шейдере. Смотрите ShaderToy.com для многих превосходных фильтров шейдеров.

...