[Applet] Rysowanie: Problem tak trywialny, że pękniecie Państwo ze śmiechu

0

Witajcie.

Jeżeli chodzi o rysowanie w Javie, jestem absolutnie początkujący. Programowałem trochę, ale raczej w formie "czarnych skrzyneczek", które miały dokonywać odpowiednich przeliczeń i transformacji obiektów - rysowanie jest mi raczej obce.

I tak - mam sobie prościutki applet. Chcę narysować obrazek, a potem go sukcesywnie zwężać aż do pewnego etapu. Problem polega na tym, że nie widzę nic (biały obszar), dopiero po zakończeniu wszystkich obliczeń pojawia się zwężony ostatecznie obrazek (ostateczny efekt). Pytanie - jak zrobić, abym mógł systematycznie obserwować zmiany, cały czas widzieć obrazek?

Z góry dzięki za pomoc.
Kod:

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;

public class Memory extends Applet {
	
	private int j=0;
	private Image img;
	private Image scaledImg;
	
	public void init()
	{
		ImageIcon imgic = new ImageIcon("tex.jpg"); // wczytujemy obrazek
		img = imgic.getImage(); // zapisujemy pierwotny obrazek
		scaledImg = img; // poczatkowo obrazek skalowany = pierwotnemu
		paint(this.getGraphics()); // rysujemy pierwotny obrazek
		cmon();
	}
	
	private void cmon()
	{
		for(j=0;j<=90;j++) 
		{
			Image tmpimg = img.getScaledInstance(100-j, 100, Image.SCALE_FAST); 
			scaledImg = tmpimg; // zmniejszamy obrazek
			repaint(); // i wyrysowujemy zmniejszony
			for(int j=0;j<1e7;j++); // (petla opozniajaca, zeby mozna bylo zobaczyc efekt)
		}
	}
	
	public void paint(Graphics g)
	{	
		g.drawImage(scaledImg, j/2, 0, this); // po prostu wyrysowujemy obrazek
	}
}
0

public void start() {

new Thread(new Runnable(){
  public void run()
  {
    cmon();
  }
}).start();

}
0

Super, dzięki uprzejme :) Jeszcze tylko podwójne buforowanie zrobić i będzie świetnie :)

Wesołych świąt Tobie i wszystkim, którzy w święta programują.
Pozdrawiam

0

A ta pętla opóźniająca to zły pomysł.

0

Lol, nawet tego nie zauważyłem.
zamiast tego czegoś zrób lepiej

try {
     Thread.sleep(30);
} catch (InterruptedException e) {}
0

Okej :) Wiem, aktywne czekanie. Proste błędy, cóż .. Na razie bardziej zależy mi na samym obracaniu :)

Tak czy inaczej - jeszcze mam problem z drugą banalną rzeczą - mianowicie owym podwójnym buforowaniem (nadal jest "flickering") - co Wy na to, czy to kwestia w/w wątku, czy też znów jakiegoś mojego prostego błędu?

public class Memory extends Applet {
	private Image scaledImg;
	private Image offscreen;
	private Graphics bufferGraphics;
	// other declarations

// other methods

public void init()
{
	offscreen = createImage(this.getSize().width, this.getSize().height);
	bufferGraphics = offscreen.getGraphics();
	//other instructions
}

public void paint(Graphics g)
{	
	bufferGraphics.clearRect(0,0,100,100);
	bufferGraphics.drawImage(scaledImg, j/2, 0, this);
	g.drawImage(offscreen, 0, 0, this);
}
	
public void update(Graphics g)
{
	paint(g);
}
0

"Flickering" jest spowodowany przez niezależne od Twojej kontroli odrysowywanie/czyszczenie okna/panelu, którego składnikiem jest clearRect z ustawionym domyślnym kolorem tła, czyli białym. Stąd problem. Częściowym rozwiązaniem go jest ustawienie Component.setIgnoreRepaint(false) i ustawienie jakiegoś timera, żeby wypuszczał regularnie wywołania żądania repaint) - o ile coś się oczywiście zmieni w oknie/panelu. Ważne aby poleceń repaint() nie wywoływać w paint lub paintComponent bo to spowoduje nieskończoną pętlę wywołać pary repaint-paint.

0

Z tym, że ja to clearRect wywołuję na buforze (Graphics bufferGraphics), który pobiera ofscreen.getGraphics(), gdzie offscreen jest obiektem typu Image, który potem rysuję za pomocą g.drawImage(offscreen, 0, 0, this) - taka jest istota podwójnego buforowania, i w ten sposób z tego co mi wiadomo jeżeli jeszcze mam przeładowaną metodę void update(Graphics g) tak, żeby wywoływała wyłącznie paint, nie powinno migotać - czy się mylę..?

0

Nie mam pojęcia co spieprzyłeś, ale ewidentnie spieprzyłeś.

Mi to nie migocze.

public class Aplet extends Applet {

	private Image offscreen;
	private Graphics2D bg;

	public void init() {
		this.setSize(200, 200);
		offscreen = createImage(this.getSize().width, this.getSize().height);
		bg = (Graphics2D) offscreen.getGraphics();

		bg.setBackground(Color.black);
		bg.clearRect(0, 0, 100, 100);
	}

	Thread t;
	
	public void start() {
		t = new Thread(new Runnable() {
			public void run() {
				try {
					while (true) {
						repaint();
						Thread.sleep(1);
					}
				} catch (InterruptedException e) {

				}
			}
		});
		t.start();
	}
	
	public void stop()
	{
		t.interrupt();
	}

	public void paint(Graphics g) {
		g.drawImage(offscreen, 0, 0, this);
	}

	public void update(Graphics g) {
		paint(g);
	}
}
0

Cóż.. Może dlatego, że nie zmieniasz nic jeżeli chodzi o obrazek? Nie wiem, dla mnie to pierwsza styczność. Zrobiłem tak, żeby to maksymalnie zunifikować z kodem który napisałeś (za co serdeczne dzięki), ale niestety, tak czy siak, migocze. Nie pozostaje mi nic więc jak wrzucić swój kod w całości chyba, niestety.

Kupiłem książkę nt. rysowania w Javie, niestety zanim dojdzie kurierem to jeszcze Was (Ciebie? ;) )trochę pomęczę - a potem mam nadzieję że ona wyjaśni przynajmniej jakąś część moich wątpliwości.

private int j=0;
	private Image image;
	private Image scaledImage;
	private Image offscreen;
	private Graphics2D bufferGraphics;
	
	public void init()
	{
		this.setSize(new Dimension(500,500));
		
		offscreen = createImage(this.getSize().width, this.getSize().height);
		bufferGraphics = (Graphics2D)offscreen.getGraphics();
		bufferGraphics.setBackground(Color.black);
		bufferGraphics.clearRect(0,0,100,100);
		
		ImageIcon imgic = new ImageIcon("tex.jpg");
		image = imgic.getImage();
		scaledImage = imgic.getImage();
	}
	
	public void start()
	{
		new Thread(new Runnable()
		{
			public void run()
			{
				try 
				{
					cmon();
				} 
				catch (InterruptedException e) 
				{
					e.printStackTrace();
				}
			}
		}
		).start();
	}
	
	public void cmon() throws InterruptedException
	{
		for(j=0;j<100;j++)
		{
			Image tmpimg = image.getScaledInstance(100-j, 100, Image.SCALE_FAST);
			scaledImage = tmpimg;
			bufferGraphics.clearRect(0,0,100,100);
			repaint();
			Thread.sleep(10);
		}
	}
	
	public void paint(Graphics g)
	{
		bufferGraphics.drawImage(scaledImage, j/2, 0, this);
		g.drawImage(offscreen, 0, 0, this);
	}
	
	public void update(Graphics g)
	{
		paint(g);
	}
0

bufferGraphics.clearRect(0,0,100,100); - to powino byćw paint() nie w cmon()

0
Keraj napisał(a)

bufferGraphics.clearRect(0,0,100,100); - to powino byćw paint() nie w cmon()

Akurat to nie robi żadnej różnicy w moim przypadku, bo wyciąganie tego z paint to jest jedna z rzeczy którą zrobiłem próbując coś zdziałać (wcześniej była wewnątrz).

0

Przyjrzałem się temu kawałkowi kodu i mam kilka spostrzeżeń

  1. DLaczego nie używaż createCompatibleImage? Wtedy bufor ma identyczny format jak ekranowy (te same plany i głębia kolorów). Jeżeli używasz createImage dostajesz format domyślny, który potem podczas rysowania musi być czasochłonnie konwertowany poza Twoją kontrolą.
  2. Dlaczego bufferGraphics.drawImage(scaledImage, j/2, 0, this); znajduje się w paint? W tej procedurze powinno znajdować się wyłącznie przerzucanie bufora na ekran lub przełączanie strony. Renderowanie może być w wątku roboczym. Nie można też zakładać, że musi skończyć się przed wyrzuceniem klatki na ekran bo CPU może być akurat na 100% zajęty - program musi uwzględniać, że dostanie za mało czasu i mimo to musi działać poprawnie gubiąc klatki lub opóźniając ich renderowanie (powtarzając wcześniej wyrenderowaną klatkę).
  3. Chcesz się nauczyć animacji w aplecie, to przejrzyj sobie przykład z użyciem SwingWorkera: http://www.java2s.com/Code/Java/Swing-Components/TumbleItemProject.htm. Są lepsze sposoby robienia animacji, ale ten jest poprawny i zgodny ze Swingiem.

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