2010-12-13 18 views
17

संपादित दोजावा: स्विंग में डबल-बफरिंग कैसे करें?

snarky टिप्पणी और एक-पंक्ति को रोकने के लिए बात को अनदेखा कर जवाब देता है: आईएफएफ यह बुला के रूप में सरल है setDoubleBuffered (सही), तो मैं कैसे वर्तमान ऑफ़लाइन बफर के लिए उपयोग मिलता है ताकि मैं BufferedImage के अंतर्निहित पिक्सेल डाटाबेसर के साथ गड़बड़ करना शुरू कर सकूं?

मैंने कोड का एक चलने वाला टुकड़ा लिखने के लिए समय निकाला (जो कि थोड़े मज़ेदार दिखता है) इसलिए मैं वास्तव में जवाब देने की प्रतिक्रिया की सराहना करता हूं (मेरा क्या झटका;) मेरा प्रश्न और समझाता है कि यह एक के बजाय क्या काम कर रहा है -लाइनर और स्नैकी टिप्पणियां;)

यहां कोड का एक कामकाजी टुकड़ा है जो एक जेएफआरएएम में एक वर्ग को उछालता है। मैं कोड के इस टुकड़े को बदलने के लिए इस्तेमाल किए जा सकने वाले विभिन्न तरीकों के बारे में जानना चाहता हूं ताकि यह डबल-बफरिंग का उपयोग कर सके।

ध्यान दें कि जिस तरह से मैं स्क्रीन साफ़ करता हूं और वर्ग को फिर से चलाता हूं, वह सबसे कुशल नहीं है लेकिन यह वास्तव में यह नहीं है कि यह प्रश्न क्या है (एक तरह से, यह इस उदाहरण के लिए बेहतर है कि यह कुछ हद तक धीमा है)।

असल में, मुझे बुफर्ड इमेज में कुछ पिक्सल को लगातार संशोधित करने की आवश्यकता है (जैसा कि कुछ प्रकार की एनीमेशन है) और मैं स्क्रीन पर एकल-बफरिंग के कारण दृश्य कलाकृतियों को देखना नहीं चाहता हूं।

मुझे एक जेएलएबल मिला है जिसका आइकन एक छवि आइकन है जिसे बुफर्ड इमेज लपेटता है। मैं उस BufferedImage को संशोधित करना चाहता हूँ।

क्या किया जाना चाहिए ताकि यह डबल-बफर हो जाए?

मैं समझता हूँ कि किसी भी तरह "छवि 1" जबकि मैं "छवि 2" पर ड्राइंग हो जाएगा दिखाया जाएगा। लेकिन फिर एक बार जब मैं "छवि 2" पर चित्रण कर रहा हूं, तो मैं "छवि 1""छवि 2" द्वारा "जल्दी" कैसे बदलूं?

क्या ऐसा कुछ है जो मुझे जेएलएबल के इमेज आइकन को स्वैप करके मैन्युअल रूप से करना चाहिए?

क्या मुझे हमेशा एक ही बुफर्ड इमेज में चित्रित करना चाहिए, तो उस BufferedImage के पिक्सेल के जेलाबेल की छवि आइकन के बुफर्ड इमेज में तेज 'ब्लिट' करें? (मुझे नहीं लगता है और मुझे नहीं लगता कि मैं मॉनीटर की "वर्टिकल रिक्त लाइन" [या फ्लैट स्क्रीन में समकक्ष) के साथ "सिंच" कैसे कर सकता हूं: मेरा मतलब है कि मॉनिटर स्वयं रीफ्रेश करने के पल के साथ हस्तक्षेप किए बिना 'सिंच' करने के लिए पिक्सल, कतरनी को रोकने के लिए])।

"repaint" आदेशों के बारे में क्या? क्या मुझे लगता है कि ये खुद को ट्रिगर करना है? मुझे/कब बिल्कुल repaint() या कुछ और कहां कॉल करना चाहिए?

सबसे महत्वपूर्ण आवश्यकता यह है कि मुझे छवियों के पिक्सेल डाटाबेस में सीधे पिक्सल संशोधित करना चाहिए।

import javax.swing.*; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.awt.image.BufferedImage; 
import java.awt.image.DataBufferInt; 

public class DemosDoubleBuffering extends JFrame { 

    private static final int WIDTH = 600; 
    private static final int HEIGHT = 400; 

    int xs = 3; 
    int ys = xs; 

    int x = 0; 
    int y = 0; 

    final int r = 80; 

    final BufferedImage bi1; 

    public static void main(final String[] args) { 
     final DemosDoubleBuffering frame = new DemosDoubleBuffering(); 
     frame.addWindowListener(new WindowAdapter() { 
      public void windowClosing(WindowEvent e) { 
       System.exit(0); 
      } 
     }); 
     frame.setSize(WIDTH, HEIGHT); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public DemosDoubleBuffering() { 
     super("Trying to do double buffering"); 
     final JLabel jl = new JLabel(); 
     bi1 = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); 
     final Thread t = new Thread(new Runnable() { 
      public void run() { 
       while (true) { 
        move(); 
        drawSquare(bi1); 
        jl.repaint(); 
        try {Thread.sleep(10);} catch (InterruptedException e) {} 
       } 
      } 
     }); 
     t.start(); 
     jl.setIcon(new ImageIcon(bi1)); 
     getContentPane().add(jl); 
    } 

    private void drawSquare(final BufferedImage bi) { 
     final int[] buf = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData(); 
     for (int i = 0; i < buf.length; i++) { 
      buf[i] = 0xFFFFFFFF; // clearing all white 
     } 
     for (int xx = 0; xx < r; xx++) { 
      for (int yy = 0; yy < r; yy++) { 
       buf[WIDTH*(yy+y)+xx+x] = 0xFF000000; 
      } 
     } 
    } 

    private void move() { 
     if (!(x + xs >= 0 && x + xs + r < bi1.getWidth())) { 
      xs = -xs; 
     } 
     if (!(y + ys >= 0 && y + ys + r < bi1.getHeight())) { 
      ys = -ys; 
     } 
     x += xs; 
     y += ys; 
    } 

} 

संपादित

यह एक पूर्ण स्क्रीन जावा आवेदन के लिए नहीं है, लेकिन एक नियमित रूप से जावा अनुप्रयोग, अपने स्वयं के (कुछ छोटे) विंडो में चल रहा है।

+2

स्विंग डिफ़ॉल्ट रूप से डबल buffered है। यह एडब्ल्यूटी नहीं है। – camickr

+0

अभी भी किसी के लिए वास्तविक कोड पोस्ट करने का इंतजार है जिसे हम जांच सकते हैं कि डबल बफरिंग प्रयास के लायक है या नहीं। – camickr

+1

मुझे लगता है कि आपने स्विंग को गलत समझा है। आप ऑफ़लाइन बफर (अनमोडिफाइड) तक नहीं पहुंच सकते हैं। स्विंग आपके माता-पिता को बफर को पीछे रखेगी, और आपके माता-पिता इसे संशोधित करेंगे। ... जो भी आप चाहते हैं उसे करने के लिए, आपको * स्विंग डबलबफरिंग को अक्षम करना होगा और कहें, BufferedImage कहें। यदि आप असली "बिटब्लेट" करना चाहते हैं, तो आपको एडब्ल्यूटी में "भारी वजन" घटक का उपयोग करना होगा। –

उत्तर

3

आम तौर पर हम कैनवास कक्षा का उपयोग करते हैं जो जावा में एनीमेशन के लिए उपयुक्त है। Anyhoo, निम्नलिखित कैसे आप डबल बफरिंग को प्राप्त है:

class CustomCanvas extends Canvas { 
    private Image dbImage; 
    private Graphics dbg; 
    int x_pos, y_pos; 

    public CustomCanvas() { 

    } 

    public void update (Graphics g) { 
    // initialize buffer 
    if (dbImage == null) { 

     dbImage = createImage (this.getSize().width, this.getSize().height); 
     dbg = dbImage.getGraphics(); 

    } 

    // clear screen in background 
    dbg.setColor (getBackground()); 
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height); 

    // draw elements in background 
    dbg.setColor (getForeground()); 
    paint (dbg); 

    // draw image on the screen 
    g.drawImage (dbImage, 0, 0, this); 
    } 

     public void paint (Graphics g) 
{ 

     g.setColor (Color.red); 



     g.fillOval (x_pos - radius, y_pos - radius, 2 * radius, 2 * radius); 

    } 
} 

अब आप एक धागा, 'रीपेंट' के बाद से x_pos और y_pos अद्यतन कर सकते हैं कैनवास वस्तु पर कॉल करें। एक ही तकनीक को जेपीनल पर भी काम करना चाहिए।

+0

@ उस्मान सलीम: हाय सलीम, यहां एक नवागंतुक की मदद करना बहुत अच्छा है :) कैनवास के साथ काम करते समय, क्या मैं एक बुफर्ड इमेज के साथ पिक्सल के डाटाबेसर तक भी पहुंच सकता हूं? – SyntaxT3rr0r

+0

ईडीटी में पुनर्विचार कॉल जारी नहीं किया जाना चाहिए? – Riduidel

+1

लेकिन इसकी आवश्यकता नहीं है। स्विंग एनीमेशन को भी अनुमति देता है। वह डबल बफरिंग नहीं है। या यदि यह एक ही कोड है जो स्विंग जेपीनेल पर उपयोग किया जा सकता है क्योंकि आप जो भी कर रहे हैं वह एक छवि खींचना है। – camickr

11

---- पिक्सेल सेटिंग प्रति संबोधित करने के लिए संपादित ----

आइटम झटका पते, डबल बफरिंग, लेकिन वहाँ भी है कि कैसे एक BufferedImage में पिक्सल प्राप्त करने पर एक मुद्दा है।

आप BufferedImage पर

WriteableRaster raster = bi.getRaster() 

फोन यदि यह एक WriteableRaster वापस आ जाएगी। वहां से आप उपयोग कर सकते हैं

int[] pixels = new int[WIDTH*HEIGHT]; 
// code to set array elements here 
raster.setPixel(0, 0, pixels); 

ध्यान दें कि आप शायद वास्तव में एक प्रतिपादन के लिए एक नई सरणी का निर्माण नहीं करने के लिए कोड को अनुकूलित करना चाहते हैं। इसके अलावा, आप शायद लूप का उपयोग न करने के लिए सरणी समाशोधन कोड को अनुकूलित करना चाहते हैं।

Arrays.fill(pixels, 0xFFFFFFFF); 

शायद आपके लूप को पृष्ठभूमि को सफेद रंग से बेहतर प्रदर्शन करेगा।

---- प्रतिक्रिया के बाद संपादित ----

कुंजी JFrame के अपने मूल सेटअप में और रन प्रतिपादन पाश अंदर है।

सबसे पहले आपको जब भी चाहें रास्टरराइजिंग रोकने के लिए SWING को बताना होगा; क्योंकि, आप इसे तब बताएंगे जब आप बफर्ड छवि पर चित्रित कर लेंगे जिसे आप पूर्ण रूप से स्वैप करना चाहते हैं। JFrame के

setIgnoreRepaint(true); 

के साथ ऐसा करें तो आप एक बफर रणनीति बनाना चाहेंगे। मूल रूप से यह निर्दिष्ट करता है कि कितने बफ़र्स आप

createBufferStrategy(2); 

उपयोग करने के लिए अब जब कि तुम बफर रणनीति बनाने की कोशिश की हैं, तो आप BufferStrategy वस्तु हड़पने के लिए के रूप में आप इसे बाद में बफ़र्स स्विच करना होगा की जरूरत है।

final BufferStrategy bufferStrategy = getBufferStrategy(); 

अपने अंदर Threadrun() पाश को संशोधित शामिल करने के लिए:

... 
    move(); 
    drawSqure(bi1); 
    Graphics g = bufferStrategy.getDrawGraphics(); 
    g.drawImage(bi1, 0, 0, null); 
    g.dispose(); 
    bufferStrategy.show(); 
... 

ग्राफिक्स bufferStrategy से पकड़ा ऑफ स्क्रीन Graphics वस्तु हो जाएगा, ट्रिपल बफरिंग बनाते समय, यह "अगले हो जाएगा "ऑफ-स्क्रीन Graphics ऑब्जेक्ट राउंड-रॉबिन फैशन में।

छवि और ग्राफिक्स संदर्भ एक रोकथाम परिदृश्य में संबंधित नहीं हैं, और आपने स्विंग को बताया कि आप स्वयं ड्राइंग करेंगे, इसलिए आपको छवि को मैन्युअल रूप से आकर्षित करना होगा। यह हमेशा एक बुरी चीज नहीं है, क्योंकि जब छवि पूरी तरह खींची जाती है (और पहले नहीं) तो आप बफर फ़्लिपिंग निर्दिष्ट कर सकते हैं।

ग्राफिक्स ऑब्जेक्ट का निपटान करना एक अच्छा विचार है क्योंकि यह कचरा संग्रह में मदद करता है।bufferStrategy दिखा रहा है बफर फ्लिप करेगा।

हालांकि उपर्युक्त कोड में कहीं भी गलत जगह हो सकती है, तो आपको वहां से 9 0% रास्ता मिलना चाहिए। सौभाग्य!

---- मूल पोस्ट इस प्रकार है ----

यह एक JavaSE ट्यूटोरियल करने के लिए इस तरह के एक सवाल का उल्लेख करने के मूर्खतापूर्ण लग सकता है लेकिन आप BufferStrategy and BufferCapatbilites में देखा है?

मुख्य मुद्दा जो मुझे लगता है कि आप सामना कर रहे हैं यह है कि आप छवि के नाम से मूर्ख हैं। BufferedImage में डबल बफरिंग के साथ कुछ लेना देना नहीं है, इसे "स्मृति में डेटा (आमतौर पर डिस्क से) बफर करना है।" इस तरह, यदि आप "डबल बफर्ड छवि" रखना चाहते हैं, तो आपको दो बुफर्ड इमेज की आवश्यकता होगी; क्योंकि छवि में पिक्सल को बदलने के लिए मूर्खतापूर्ण है जो दिखाया जा रहा है (इससे समस्याएं उत्पन्न हो सकती हैं)।

अपने प्रतिपादन कोड में, आप ग्राफिक्स ऑब्जेक्ट को पकड़ते हैं। यदि आप ऊपर दिए गए ट्यूटोरियल के अनुसार डबल बफरिंग सेट अप करते हैं, तो इसका मतलब है कि आप ऑफ-स्क्रीन Graphics ऑब्जेक्ट को (डिफ़ॉल्ट रूप से) ले लेंगे, और सभी ड्राइंग ऑफ़-स्क्रीन होगी। फिर आप ऑफ-स्क्रीन ऑब्जेक्ट पर अपनी छवि (बिल्कुल सही) खींचते हैं। अंत में, आप show() बफर की रणनीति बताते हैं, और यह आपके लिए ग्राफिक्स संदर्भ के प्रतिस्थापन करेगा।

+0

@ एडविन बक: सहायता के लिए +1 लेकिन मैं बुफर्ड इमेज का उपयोग नहीं करता क्योंकि नाम मुझे बेवकूफ बना देगा: मैं उनका उपयोग कर रहा हूं क्योंकि छवि में सीधे पिक्सेल में हेरफेर करने की क्षमता एक आवश्यकता है और मुझे पता है कि एकमात्र तरीका किसी छवि के अंतर्निहित पिक्सेल डाटाबेसर तक पहुंच प्राप्त करना BufferedImage का उपयोग करना है। – SyntaxT3rr0r

+0

@ स्प्ूनबेंडर अच्छी तरह से, मैं सोच रहा था कि आपको दो बुफर्ड इमेज की आवश्यकता होगी, वास्तव में आप केवल एक के साथ प्राप्त कर सकते हैं क्योंकि आपको इसे प्रत्येक बफर में व्यक्तिगत रूप से आकर्षित करने की आवश्यकता है। मैंने आपके पोस्ट को अपडेट किया है कि उदाहरण में स्विंग के डबल बफरिंग सपोर्ट को संभवतः कैसे एकीकृत किया जाए। –

+0

यह शायद एक बेहतर रणनीति है जिसे मैंने पोस्ट किया था। यह वही तकनीक भी कई बफर के लिए कैनवास कक्षा द्वारा उपयोग की जाती है (और मुझे लगता है कि यह तकनीक है जिसे मैंने जावा में अपने छात्रों के लिए एक एनीमेशन प्रोजेक्ट पर बहुत समय पहले इस्तेमाल किया था)। समर्थन के वोट के लिए –

3

यहां एक भिन्नता है जिसमें सभी चित्र event dispatch thread पर होते हैं।

परिशिष्ट:

असल में, मैं लगातार एक BufferedImage में एक बहुत पिक्सल संशोधित करने की आवश्यकता ...

यह kinetic model पिक्सेल एनीमेशन के लिए कई दृष्टिकोण को दिखाता है।

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics2D; 
import java.awt.GridLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.*; 
import java.awt.image.BufferedImage; 

/** @see http://stackoverflow.com/questions/4430356 */ 
public class DemosDoubleBuffering extends JPanel implements ActionListener { 

    private static final int W = 600; 
    private static final int H = 400; 
    private static final int r = 80; 
    private int xs = 3; 
    private int ys = xs; 
    private int x = 0; 
    private int y = 0; 
    private final BufferedImage bi; 
    private final JLabel jl = new JLabel(); 
    private final Timer t = new Timer(10, this); 

    public static void main(final String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       JFrame frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new DemosDoubleBuffering()); 
       frame.pack(); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public DemosDoubleBuffering() { 
     super(true); 
     this.setLayout(new GridLayout()); 
     this.setPreferredSize(new Dimension(W, H)); 
     bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB); 
     jl.setIcon(new ImageIcon(bi)); 
     this.add(jl); 
     t.start(); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     move(); 
     drawSquare(bi); 
     jl.repaint(); 
    } 

    private void drawSquare(final BufferedImage bi) { 
     Graphics2D g = bi.createGraphics(); 
     g.setColor(Color.white); 
     g.fillRect(0, 0, W, H); 
     g.setColor(Color.blue); 
     g.fillRect(x, y, r, r); 
     g.dispose(); 
    } 

    private void move() { 
     if (!(x + xs >= 0 && x + xs + r < bi.getWidth())) { 
      xs = -xs; 
     } 
     if (!(y + ys >= 0 && y + ys + r < bi.getHeight())) { 
      ys = -ys; 
     } 
     x += xs; 
     y += ys; 
    } 
} 
3

आप जो चाहते हैं वह स्विंग के साथ विंडो मोड में मूल रूप से असंभव है। विंडो पश्चाताप के लिए रास्टर सिंक्रनाइज़ेशन के लिए कोई समर्थन नहीं है, यह केवल पूर्णस्क्रीन मोड में उपलब्ध है (और फिर भी सभी प्लेटफ़ॉर्म द्वारा समर्थित नहीं हो सकता है)।

स्विंग घटकों को डिफ़ॉल्ट रूप से डबल-बफर किया जाता है, यह है कि वे एक इंटरमीडिएट बफर के लिए सभी प्रतिपादन करेंगे और अंततः बफर को स्क्रीन पर कॉपी किया जाएगा, पृष्ठभूमि समाशोधन से झिलमिलाहट से परहेज किया जाएगा और फिर इसके शीर्ष पर चित्रण किया जाएगा। और यह एकमात्र रणनीति है जो सभी अंतर्निहित प्लेटफार्मों पर उचित रूप से समर्थित है। यह केवल झटके से छेड़छाड़ से बचाता है, लेकिन ग्राफिक तत्वों को स्थानांतरित करने से दृश्य फाड़ नहीं जाता है।

आपके नियंत्रण में पूरी तरह से किसी क्षेत्र के कच्चे पिक्सेल तक पहुंचने का एक आसान तरीका है, जो कि क्यूपोनेंटेंट से एक कस्टम घटक का विस्तार करेगा और इसके पेंट कॉम्पोनेंट() - विधि को बफर्ड इमेज (मेमोरी से) से पेंट करने के लिए ओवरराइट करेगा। :

public class PixelBufferComponent extends JComponent { 

    private BufferedImage bufferImage; 

    public PixelBufferComponent(int width, int height) { 
     bufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
     setPreferredSize(new Dimension(width, height)); 
    } 

    public void paintComponent(Graphics g) { 
     g.drawImage(bufferImage, 0, 0, null); 
    } 

} 

फिर आप अपनी इच्छानुसार छवि को बफर कर सकते हैं। स्क्रीन पर अपने बदलाव दिखाई देने के लिए, बस उस पर repaint() को कॉल करें। यदि आप ईडीटी के अलावा किसी थ्रेड से पिक्सेल मैनिपुलेशन करते हैं, तो आपको वास्तविक प्रतिबिंब और आपके हेरफेर थ्रेड के बीच दौड़ की स्थिति का सामना करने के लिए दो बफर छवियों की आवश्यकता होती है।

ध्यान दें कि यह कंकाल घटक के पूरे क्षेत्र को पेंट नहीं करेगा जब लेआउट मैनेजर के साथ प्रयोग किया जाता है जो घटक को अपने पसंदीदा आकार से परे खींचता है।

नोट भी, बफर्ड छवि दृष्टिकोण अधिकतर समझ में आता है यदि आप छवि पर setRGB (...) के माध्यम से वास्तविक निम्न स्तर पिक्सेल मैनिपुलेशन करते हैं या यदि आप सीधे अंतर्निहित डेटाबफर तक पहुंचते हैं। यदि आप ग्राफिक्स 2 डी के तरीकों का उपयोग करके सभी कुशलताएं कर सकते हैं, तो आप उपलब्ध ग्राफिक्स (जो वास्तव में एक ग्राफिक्स 2 डी है) का उपयोग करके पेंट कॉम्पोनेंट विधि में सभी चीजें कर सकते हैं और इसे आसानी से जाया जा सकता है)।

संबंधित मुद्दे