Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
ich habe ein Programm, wo immer große Bilder in Hintergrund gelegt werden. Diese sind ~640x480 Pixel groß.
Jetzt besteht das Problem, das wenn ich die Panels mit den Bilder zum ersten mal aufrufe, es eine ganz kurze Zeit dauert, bis diese dagestellt werden. Beim zweiten mal Aufrufen sind diese soft da.
Also als ob sie "vorgeladen sind" Kann ich dieses Vorladen auch selber realisieren? Damit der Benutzer von Anfang an die Bilder sieht, ohne die Wartezeit?
package imoy;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
public class Textureloader{
private Image texture[];
public Image[] loadtexture(String pfad){
try {
BufferedReader ls = new BufferedReader(new FileReader(pfad));
int anz=0;
while ((ls.readLine()) != null)anz++;
BufferedReader x = new BufferedReader(new FileReader(pfad));
texture=new Image[anz];
for(int i=0;i<anz;++i){
pfad=x.readLine();
texture[i]=Toolkit.getDefaultToolkit().getImage(pfad);
}
} catch (Exception e) {
System.out.println("Fehler beim Laden der Texture-Datei!");
}
return texture;
}
}
Im Moment sieht das so aus einfach ein IMAGE[]. Im Programm kann ich die dann über denn Index aufrufen.
(ja Vector wäre besser kannte ich aber damals noch nicht ;-) )
im prinzip musst du das laden des panels vom laden der bilder abkoppeln. das realisiert man sinnvollerweise über einen thread. die konstruktion müste so aussehen, daß du bei programmstart bzw. sobald dein hier gepostetes parameter pfad vorliegt, du einen extra thread mit dem laden der bilder beauftragst. wenn dann dein panel (deine view) geladen werden soll, checkt diese, ob das bild, welches sie anzeigen soll, bereits vollständig geladen wurde. wenn ja, zeigt sie dieses an, ansonsten wartet sie bis das bild da ist.
um das in deinem programm zu realisieren musst du deinen Texturloader von Thread erben lassen. am besten baust du dazu einen konstruktor, der als parameter den pfad übergeben bekommt, damit sichergestellt ist, daß immer ein pfad vorhanden ist. überschreibe dann die run() methode mit dem code, den du hier in der methode loadtexture(...) gepostet hast. dabei wäre es natürlich schöner, wenn das parameter pfad, welches ja dann eine globale variable ist, von der methode nicht ständig geändert wird. das könnte ein lokale variable besser erledigen und dein texturloader-thread kann jederzeit, wenn er einen neuen pfad zum laden erhält, überprüfen, ob das überhaupt notwendig ist, weil ggf. von diesem pfad bereits geladen wurde. zu alle dem lege in dem texturloader-thread paralel zum array texture noch ein globales array isLoaded[] an. beide array's werden beim laden von bildern auf gleiche länge getrimmt, nämlich auf die anzahl der bilder. wenn deine run-methode ein bild geladen hat, setzt du den entsprechenden index in isLoaded[] auf true. erzeuge zusätzlich eine synchronized methode in der art getImage(int index). in diese methode implementierst du zunächst eine überprüfung, ob überhaupt dieser index jemals existieren kann (weil ggf. gar nicht soviele bilder vorhanden sind ... kleiner schutz vor programmierfehlern), dann eine überprüfung von isLoaded. wenn isLoaded = false, springst du in eine warteschleife, in dieser art:
Code:
...
while( !isLoaded[index] )
try {
sleep(100); // angabe in millisekunden. alternativ kannst du auch wait(...) aufrufen.
}
catch( Exception e ){
e.printStacktrace();
}
abschließend gibst du dann das entsprechende bild zurück.
ausserdem hier noch ein wichtiger hinweis für dich. du kannst ernsthafte probleme bekommen, wenn du die bilder so lädst, ohne zu überprüfen, ob das bild auch wirklich fertig geladen wurde. wenn du mal mit System.currentTimeMillis() dir ansiehst, wie lange der aufruf Toolkit.getDefaultToolkit().getImage(pfad) benötigt, wirst du feststellen, daß hier überhaupt keine zeit vergeht. das liegt daran, daß dieser aufruf das bild asynchron läd. deshalb hatte L-ectron-X wohl auch auf den MediaTracker verwiesen. dieser bietet dir die ideale möglichkeit ein bild aufs vollständige laden zu prüfen. durch den MediaTracker kannst du dir sogar das array isLoaded[] sparen. lege einfach global ein MediaTracker-objekt an und füge deine bilder nach dem laden mit dem Toolkit diesem hinzu (mit fortlaufendem index, also dem gleichen index, den du in deinem ganzen programm für die bilder verwenden willst. mittels der methode checkID(int) des MediaTrackers kannst du das gleiche tun, was ich oben mit isLoaded[index] gemacht habe, oder du verwendest gleich, anstatt dieses ganzen blocks den aufruf waitForID(int).
beachte übrigends ausserdem, daß dir nicht unendlich viel speicher zur verfügung steht, du also ggf. mal bilder wieder aus dem speicher rauswerfen wirst müssen, falls zuviele zu laden sind. ohnehin würde ich anstatt eines arrays eher einen Vector oder eine ArrayList zum halten der bilder verwenden.
Hm...also liegt es daran, dass das Image nicht wirklich geladen wurde, sondern nur der Pfad bereit gestellt wurde. Weil wie gesagt, wenns einmal aufgerufen wurde, dann gehts.
Die Bilder sind meist nur 100kb groß. Auch wenns am Ende weit über 500 sind, so werde ich nicht mal die 50MB grenze überschreiten...denke das normal ;-)
Im moment siehts so aus mit dem erstellen, der panels + Bilder. Hatte früher mal es über paint() realisiert, wovon ich mich aber verabschiedet habe, weils zu umständlich war...jetzt sinds einfach nur Labels.
Ah glaube ich habs...es ruft denn Textureloader immer nach jedem Panel wechsel auf...das natürlich gleich zwei mal doof. Ich bau das eh nochmal alles um. Das muss noch besser gehen, viel besser.
jo, bastel noch etwas daran. hab auch schon vielfach die erfahrung machen müssen, daß es in java zig wege gibt um mit bildern umzugehen, aber einen performaten sinnvollen weg zu erarbeiten, dauert seine zeit.
lass mich dich aber an dieser stelle noch schnell korrigieren, bezüglich der größe deiner bilder im speicher: wie du schreibst, sind deine bilder alle um die 640x480 pixel groß. nach dem laden liegen sie dir als Image vor. dabei kannst du davon ausgehen, daß jedes pixel in einem int abgespeichert wird, womit dann für R-, G- und B-kanal jeweils 8-bit zur verfügung stehen und ggf. noch 8-bit platz für einen alpha-kanal wäre. somit hat jedes pixel also 2 byte. 640 * 480 * 2 = 614400 byte, also ca. 600 KB. das heißt also, daß dein speicher mit ca. 100 bildern voll ist, sofern du ihn nicht mittels java-start-parameter -Xmx erweiterst.
du kannst die größe der bilder natürlich verringern. da das aber rechenaufwand bedeutet, empfehle ich dir, dies ebenfalls in einem seperaten thread im hintergrund zu erledigen. verringern kannst du die größe beispielsweise, indem du das farbmodel der bilder änderst. hierfür bietet sich ein PackedColorModel oder noch besser ein IndexColorModel an. zum PackedColorModel kann ich nicht viel sagen, für ein IndexColorModel zählst du zunächst die unterschiedlichen farben ab und legst diese in eine liste. anschließend ersetzt du die pixelwerte durch die indizes der farben. dadurch werden die bilder in der regel wesentlich kleiner, abhängig von der anzahl der verschiedenen farben. hätte ich das zu programmieren, würde ich zunächst meine view anfragen lassen, ob das verlangte bild komprimiert vorliegt. ist das nicht der fall, sollte sie das unkompimierte bild anzeigen, bis das komprimierte bild vorhanden ist (benachrichtigung per listener auf den farbmodel-ändernden-thread). zu dem ganzen würde ich mir ein ImageModel basteln, welches die geladenen bilder sowie die farbmodel-geänderten halten kann und welches diese von den beiden threads (dem bilder-ladenden und dem farbmodel-änderndem) erhält. wenn das model ein farbmodel-geändertes bild erhält, ersetzt es das ursprünglich geladene, um speicher einzusparen. am besten ruft es bei der gelegenheit gleich System.gc() auf, damit dann der speicher auch möglichst zeitnah freigegeben wird.
Im Windows sind die auch nur 100kb groß. Werden die nicht einfach immer über denn pfad geladen?
Kann ja mal aus spaß 100x das selbe Bild reinlegen und schauen obs mir explodiert :lol:
Also der Textureloader kann auch gern 150 640x480 Bilder laden ;-) das dem egal...
nur wenn ich die verschiedenen panels aufrufe merke ich gerade steigt der speicher langsam an..fält zwar auch wieder am wenn ganz schnell machst geht das schnell von 23MB auf 31 hoch
klar haben die unter windows, wie auch unter anderen betriebssystemen nur 100KB, komprimiert. aber wenn du die in java einlädst, werden sie entpackt. es sei denn, du hast sie als BMP-dateien herumliegen, die sind häufig vollständig unkomprimiert. aber sonst ... GIF ist häufig schon durch sein indiziertes farbmodel komprimiert, JPEG sowieso durch diskrete cosinus-transformationen ... ... ...
ansich, wenn du den MediaTracker verwendest, damit dein programm wartet, bis das jeweilige bild vollständig geladen wurde, sollte nach dem laden jedes bildes der speicherbedarf deines programms sich entsprechend erhöhen. ich persönlich verwende meißtens com.sun.image.codec.jpeg.JPEGImageDecoder zum laden von JPEGs, da dieser das bild gleich als BufferedImage zurückgibt, die methode ohnehin erst dann wiederkehrt, wenn das bild vollständig geladen wurde und ich bilder auch über zeichenketten laden kann, wenn sie beispielsweise einer datenbank entspringen. bei dem ist das so ... logischerweise. denn jedes bild befindet sich nach dem laden vollständig im speicher. und genau das ist es ja auch, was ich haben will, sonst benötigt mein programm zeit bis das bild aufgebaut ist. bist du sicher, daß deine bilder vollständig geladen wurden? sprich, hast du den MediaTracker benutzt um das abzuklären?
aber eh egal. ob du das mit der kompression im speicher machst oder nicht bleibt dir überlassen. hängt von den fakten ab, ob die anzahl der bilder jemals einen kritischen wert erreichen wird und ob du beim benutzer nicht noch beispielsweise die startoption -Xmx255m zum startbefehl hinzufügst. wenn dir 64MB (standardwert des speichers für java-anwendungen) für mehr als 150 bilder ausreichen und du niemals mehr als 500 bilder gleichzeitig im speicher haben wirst sollten es 255MB tun.