定期刷新BufferedImage

时间:2014-01-19 14:04:48

标签: java swing rgb bufferedimage swingworker

BufferedImage中显示JFrame,我想定期刷新我通过Socket(byte[] buffer)收到的原始R,G,B数据。操作顺序应如下所示:

  1. 接收纯RGB数据的字节[1280 * 480]阵列(每个分量一个字节) - >这部分完美无缺
  2. 遍历字节数组并为每个像素调用BufferedImage.setRgb(x, y, RGB)
  3. 我接收和显示一帧没有问题,但是当我包装执行步骤1和2的代码时,我会定期接收数据但是没有显示单帧。我的猜测是,接收数据的速度明显快于显示图像,换句话说,我接收到的图像会以某种方式被新图像等覆盖。 我的下一个想法是将带有图像的缓冲区移交给另一个后台线程并阻止主线程进行网络通信,直到后台线程完成“显示”图像。 然后我听说可以很容易地使用SwingWorker在这里完成:Can a progress bar be used in a class outside main?但它完全一样,就像我仍然在一个线程上做所有事情:没有显示任何图像。这是我的代码:

    public class ConnectionManager {
    
    public static final String serverIp = "192.168.1.10";
    public static final int tcpPort = 7;
    public static final int bufferSize = 1280;
    
    private Socket client;
    private BufferedInputStream networkReader;
    private PrintStream printStream;
    
    byte[] buffer;
    
    public ConnectionManager(){}
    
    public void connect() throws IOException{
        int dataRead;
        while(true){
            client = new Socket(serverIp, tcpPort);
    
            printStream = new PrintStream(client.getOutputStream());
            networkReader = new BufferedInputStream(client.getInputStream());
    
            dataRead = 0;
            buffer = new byte[1280 * 480];
            printStream.println(""); // CR is code to server to send data
    
    
            while(dataRead < (1280 * 480)){
                dataRead += networkReader.read(buffer, dataRead, (1280 * 480) - dataRead);
            }
    
            DrawBack d = new DrawBack();
            d.execute();
            try {
                d.get(); // here im trying to block main thread purposely
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } 
    
        }
    }
    
    private class DrawBack extends SwingWorker<Void, Void>{
    
        @Override
        protected Void doInBackground() throws Exception {
            byte Y, U, V;
            int R, G, B, RGB, Yi, Ui, Vi;
            boolean alternate = false;
            for(int i = 0; i < 480; ++i){
                for(int j = 1; j < 1280; j += 2){
                    if(alternate){
                        Y = buffer[i * 1280 + j];
                        V = buffer[i * 1280 + j -1];
                        U = buffer[i * 1280 + j -3];
                    } else {
                        Y = buffer[i * 1280 + j];
                        U = buffer[i * 1280 + j -1];
                        V = buffer[i * 1280 + j +1];
                    }
                    Yi = Y & 0xFF;
                    Ui = U & 0xFF;
                    Vi = V & 0xFF;
    
                    R = (int)(Yi + 1.402 * (Vi - 128));
                    G = (int)(Yi - 0.34414 * (Ui - 128) - 0.71414 * (Vi - 128));
                    B = (int)(Yi + 1.772 * (Ui - 128));
    
                    RGB = R;
                    RGB = (RGB << 8) + G;
                    RGB = (RGB << 8) + B;
    
                    alternate = !alternate;
    
                    Masapp.window.getImage().setRGB(j/2, i, RGB);// reference to buffered image on JFrame
    
                    if((i == 100) && (j == 479)){
                        System.out.println(Yi + " " + Ui + " " + Vi);
                    }
                }
            }
            return null;
        }
    }
    }
    

    我甚至试图用while来等待完成:

    DrawBack d = new DrawBack(); // DrawBack extends SwingWorker<Void, Void>
    d.execute();
    while(!d.isDone());
    

    但没有改善。我在设置所有像素时尝试调用BufferedImage.flush()JFrame.invalidate()

    我的问题主要是:如何定期刷新和显示缓冲图像?

1 个答案:

答案 0 :(得分:3)

您的实现未正确同步,因为它从工作人员的后台线程而不是event dispatch thread更新GUI。由此产生的行为是不可预测的。相反,请定义SwingWorker<BufferedImage, BufferedImage>publish()图像,以便稍后在process()的实现中呈现。为了提高活力,请在图像准备就绪时发布部分图像,例如publish()一个BufferedImage一次包含一行。将引用的example与此相关example进行比较,看看方法。