Animacja w przeglądarce i w appletviewer

0

Oto kod przykładowego appletu:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import javax.swing.JApplet;
 
/**
 *
 */
public class NewJApplet extends JApplet implements Runnable {
 
    int frame;
    int delay;
    Thread animator;
 
    Dimension offDimension;
    Image offImage;
    Graphics2D offGraphics;
 
    /**
     * Initialization method that will be called after the applet is loaded
     * into the browser.
     */
    @Override
    public void init() {
        //String str = getParameter("fps");
        //int fps = (str != null) ? Integer.parseInt(str) : 10;
        //delay = (fps > 0) ? (1000 / fps) : 100;
        int fps = 10;
        delay = 10;
        GraphicsEnvironment graph = GraphicsEnvironment.getLocalGraphicsEnvironment();
        setSize(graph.getMaximumWindowBounds().getSize());
    }
 
    // TODO overwrite start(), stop() and destroy() methods
 
    /**
     * This method is called when the applet becomes visible on
     * the screen. Create a thread and start it.
     */
    @Override
    public void start() {
        animator = new Thread(this);
        animator.start();
    }
 
    /**
     * This method is called by the thread that was created in
     * the start method. It does the main animation.
     */
    public void run() {
        // Remember the starting time
        long tm = System.currentTimeMillis();
        while (Thread.currentThread() == animator) {
            // Display the next frame of animation.
            //this.repaint(100, 100, 50, 50);
            repaint();
 
            // Delay depending on how far we are behind.
            try {
                tm += delay;
                Thread.sleep(Math.max(0, tm - System.currentTimeMillis()));
            } catch (InterruptedException e) {
                break;
            }
 
            // Advance the frame
            frame++;
        }
    }
 
    /**
     * This method is called when the applet is no longer
     * visible. Set the animator variable to null so that the
     * thread will exit before displaying the next frame.
     */
    @Override
    public void stop() {
        animator = null;
        offImage = null;
        offGraphics = null;
    }
 
    private void antiAliasing(){
        offGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }
 
 
    /**
     * Update a frame of animation.
     */
    
    @Override
    public void update(Graphics g) {
        Dimension d = size();
 
        // Create the offscreen graphics context
        if ((offGraphics == null)
         || (d.width != offDimension.width)
         || (d.height != offDimension.height)) {
            offDimension = d;
            offImage = createImage(d.width, d.height);
            offGraphics = (Graphics2D)offImage.getGraphics();
        }
 
        // Erase the previous image
        offGraphics.setColor(getBackground());
        offGraphics.fillRect(0, 0, d.width, d.height);
        offGraphics.setColor(Color.black);
 
        // Paint the frame into the image
        paintFrame(offGraphics);
 
        // Paint the image onto the screen
        g.drawImage(offImage, 0, 0, null);
    }
 
    /**
     * Paint the previous frame (if any).
     */
    
    @Override
    public void paint(Graphics g) {
        update(g);
    }
 
    /**
     * Paint a frame of animation.
     */
    public void paintFrame(Graphics2D g) {
        Dimension d = size();
        int h = d.height / 2;
        for (int x = 0 ; x < d.width ; x++) {
            int y1 = (int)((1.0 + Math.sin((x - frame) * 0.05)) * h);
            int y2 = (int)((1.0 + Math.sin((x + frame) * 0.07)) * h);
            antiAliasing();
            g.drawLine(x, y1, x, y2);
        }
    }
}

Problem tkwi w animacji. W przeglądarce wyświetla płynnie i ładnie, a w appletviewer wygląda to bardzo źle i miga... Dlaczego tak się dzieje?

Oto link do appletu: http://kni.prz.edu.pl/~bloodorder/applet/applet

0

Pewnie dlatego, że ten kawałek kodu nie kontroluje przebiegów czasowych oraz nawet nie próbuje uzgodnić formatu graficznego offimage z bieżącym trybem graficznym.
Spróbuj zamienić offImage = createImage(d.width, d.height) na użycie createCompatibleImage().
Oprócz tego jeżeli nie będzie można mimo wszystko uzgodnić sprzętowego wspomagania (co dzieje się za plecami Javy i Swinga), to trzeba będzie zawsze sprawdzać czy aby przepisywanie obrazu z offimage na ekran za pomocą g.drawImage(offImage, 0, 0, null) nie jest po prostu zbyt czasochłonne. Natomiast na pewno zbyt czasochłonne wydają się obliczenia z użyciem dwóch wywołań funkcji sinus i czterech mnożeń wywoływane tyle razy ile szerokość okna animacji w pikselach (np. 800). Takich obliczeń do renderowania absolutnie nie przeprowadza się w metodzie paint(). Do tego tworzy się inny wątek (uwzględniając synchronizację dostępu do danych), albo dodatkowo redukuje dynamicznie FPS. Ten kod tego nie robi, więc co się dziwić, że w niektórych środowiskach chodzi źle.
Najlepiej jakby w paint jedynie kopiowano ostatni wyrenderowany w całości offimage. Krótko mówiąc musiałbyś wyrzucić paintFrame z metody update() i redukować liczbę repaint() do takich ilości, które gwarantowałyby zmieszczenie pewnej liczby renderów w jednej sekundzie. Ilości zależnej od szybkości pojedynczego renderowania offimage na konkretnej maszynie lub środowisku i z konkretną rozdzielczością (wielkością okna).

1 użytkowników online, w tym zalogowanych: 0, gości: 1