1. Nimm jetzt an unserem 2. ADVENT-Gewinnspiel teil - Alle Informationen findest Du hier!

Rendering von Bitmaps

Dieses Thema im Forum "Android App Entwicklung" wurde erstellt von Duckemai, 27.06.2011.

  1. Duckemai, 27.06.2011 #1
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Hallo zusammen,

    ich versuche derzeit ein kleines Spiel zu programmieren. Dafür habe ich bitmaps auf dem Screen, die ich hin und her drehe und über den Bildschirm laufen lasse. Da die Images der Bitmaps gerade Ränder haben, sehe ich nun, das die Ränder der Bitmapzeichnung beim Verschieben über das Hintergrundbild immer wie abgebrochen wirken. Also eine lange Linie nach dem Drehen in vielleicht 3 schräg laufende Linien unterteilt wird, mit kantigen Unterbrechungen entsprechend der Pixel. Kann man die Bitmaps in Android auch rendern, so dass weiche Ränder entstehen. Kennt jemand das passende Stichwort dazu oder besser noch ein Tutorial?

    Vielen Dank
    Duckemai
     
    Zuletzt bearbeitet: 27.06.2011
  2. v Ralle v, 27.06.2011 #2
    v Ralle v

    v Ralle v Android-Lexikon

    Beiträge:
    913
    Erhaltene Danke:
    199
    Registriert seit:
    27.08.2010
    Das Fachwort dazu ist Antialiasing. Google mal danach, vielleicht hilft. Ich selber habe keine Erfahrung dazu mit einer Bitmap.
     
    Duckemai bedankt sich.
  3. Duckemai, 28.06.2011 #3
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Ja perfekt! Genau das war das Stichwort. Das Rendering klappt nun super,
    leider ruckeln die Bitmaps nun. Habe jetzt schon viel über invalidate() gelesen, aber wirklich funktionieren tut es nicht.

    Da heißt es, wer einen Thread benutzt muss invalidate() nicht aufrufen. Ich
    benutze einen, Bitmaps ruckeln aber. Auch habe ich sowohl den Aufruf in einer onDraw() und einer doDraw() versucht. (Ich benutze eine SurfaceView). Invalidate() bringt das System zum Absturz, bei postInvalidate() ruckeln die Bitmaps munter weiter.

    Kann mir jemand sagen, wo das invalidate/postinvalidate denn nun hingehört, damit es funktioniert? Am besten bei Benutzung der doDraw().

    Vielen Dank,
    Duckemai
     
  4. DieGoldeneMitte, 28.06.2011 #4
    DieGoldeneMitte

    DieGoldeneMitte Android-Lexikon

    Beiträge:
    1,230
    Erhaltene Danke:
    256
    Registriert seit:
    05.02.2010
    Phone:
    Nexus 5X
    Tablet:
    Nexus 7 (2013)
    Ohne detaillierte Codekenntnis ist das kaum zu beantworten.

    Aber allgemein fährt man mit Off-Screen-Rendering ruckelfreier.
    D.h. du malst in eine Bitmap hinein (die erstmal nicht auf dem Bildschirm
    landet) und diese Bitmap kannst Du dann in deinem SurfaceView (zB in einem
    extra Thread) mit einer Framerate deiner Wahl en bloc schreiben.
     
    Duckemai bedankt sich.
  5. Duckemai, 28.06.2011 #5
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Das ist ein sehr interessantes Stichwort. Habe gleich mal Google angeworfen um vielleicht so etwas wie ein Tutorial zu finden. Leider scheint Off-Screen-Rendering vorwiegend für OpenGL sehr populär zu sein. Ich arbeite aber mit Java2D. Kennst Du vielleicht ein Tutorial dazu?

    Vielen Dank
    Duckemai
     
  6. DieGoldeneMitte, 29.06.2011 #6
    DieGoldeneMitte

    DieGoldeneMitte Android-Lexikon

    Beiträge:
    1,230
    Erhaltene Danke:
    256
    Registriert seit:
    05.02.2010
    Phone:
    Nexus 5X
    Tablet:
    Nexus 7 (2013)
    Das eigentlich zu simpel für ein Tutorial.

    Also im Prizip geht das so (aus der Hüfte geschossen):

    1.) ein eigener Canvas:
    Code:
    package my.package;
    
    class OffScreenView extends View {
       public OffScreenView(Context context, AttributeSet attrs) {
         super(context, attrs);
       }
    
       Bitmap buffer = null;
       Canvas offScreen = null;
    
       public void onDraw( Canvas c ) {
         if( buffer==null ) {
          buffer = Bitmap.createBitmap( c.getWidth(), c.getHeight(), Bitmap.Config.ARGB_8888 );
          offScreen = new Canvas(buffer);
         }
         c.drawBitmap(buffer,0,0,null);
       }
    
    }
    
    Nun malst du mit offScreen anstatt mit c. Und machst kein postInvalidate oder so!

    2.) Im Lauyout definieren:
    Code:
    <my.package.OffScreenView
        android:layout_width="..."
        ...
       android:id="@+id/offscreen"
    />
    3.) Update thread bauen:

    Code:
    class Updater implements Runnable {
      OffScreenView theView;
      boolean running;
      public Updater( OffScreenView theView ) { this.theView = theView; ... }
      public void run() {
         while( running ) {
            theView.postInvalidate();
            try { Thread.sleep( 1000 / FRAMERATE ); } catch( InterruptedException wont_care ) {}
         }
      }
    }
    4.) In der Activity den Updater einbinden:

    Code:
    ....MyActivity...
    
       private Updater upd;
    
       public void onCreate( Bundle bundle ) {
         super.onCreate(nundle);
         upd = new Updater( (OffScreenView)findViewById(R.id.offscreen)) );
         ...
      }
    
       public void onResume() {
          super.onResume();
          new Thread(upd).start();
       }
          
       public void onPause() {
          super.onPause();
          upd.running = false;
       }
    
     
    Zuletzt bearbeitet: 29.06.2011
    Duckemai bedankt sich.
  7. Duckemai, 29.06.2011 #7
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Wow!!! Vielen Dank für die Mühe. Ich versuche es gleich mal und gebe Feedback.
     
  8. Duckemai, 30.06.2011 #8
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Tja, was soll ich sagen. Ich habe erstmal deine Ausarbeitungen 1 zu 1 übernommen. Ich war ganz überrascht, dass das Spiel mit Deinem Vorschlag ohne zu murren startete. (Respekt! Wer so aus der Hüfte schießen kann, der braucht auch nicht mehr zu zielen! :thumbup:)

    Ich habe es durchdebugged.
    Die Anwendung läuft durch den start() im Activity,
    dann durch Bitmap.createBitmap in der OffScreenView und durch die drawBitmap der OffScreenView. Aber alles nur einmal und dann nie wieder. :crying:

    Also habe ich boolean running im Updater auf true initialisiert. Jetzt sieht
    es schon anders aus:
    Die Anwendung läuft durch den start() im Activity,
    jetzt endlich auch durch theView.postInvalidate() im Updater
    dann durch Bitmap.createBitmapin der OffScreenView
    und ab hier läuft er im Wechsel zwischen postInvalidate() und drawBitmap.

    Sah aus, als würde es klappen. Doch die Bitmaps ruckelten mehr als vorher. :ohmy:


    Der Durchlaufen von Hauptthread(HT), postInvalidate(pI) und Buffer_onDraw(B_oD) sieht in etwa so aus:
    1 x HT
    1-3 x pI
    1 x B_oD
    ...und wieder von vorne.

    Ich habe dann mit dem Thread.sleep herumgespielt. Mal hoch gesetzt, mal
    runter. Mal ausgeschaltet. Irgendwie scheint mein Hauptthread, dadurch dass ich ihn auf FPS begrenze, das Ruckeln mitzuverursachen. Denn dort gibt es ebenfalls einen Thread.sleep. Womit ich wieder bei dem leidigen Thema einer GameLoop bin. Da habe ich noch nichts gefunden, das ein schnelles System ruckelfrei ausbremsen kann.

    Gruß
    Duckemai
     
    Zuletzt bearbeitet: 30.06.2011
  9. DieGoldeneMitte, 30.06.2011 #9
    DieGoldeneMitte

    DieGoldeneMitte Android-Lexikon

    Beiträge:
    1,230
    Erhaltene Danke:
    256
    Registriert seit:
    05.02.2010
    Phone:
    Nexus 5X
    Tablet:
    Nexus 7 (2013)
    Was soll das sein?
    Ein Stacktrace oder die Threadliste im Debugger?
    Was ist "Hauptthread"?
    Kann es sein, dass du mehrere Updater am laufen hast?

    Ich weiss auch nicht, wo du in den offscreen reinmalst. Das sollte
    nicht aus dem Code aus meinem Beispiel sein, sondern
    in einem Extrathread (das wäre dann im Prinzip die Gameloop. Achtung:
    auch davon darf nur eine Laufen). Übrigens ein Thread.yield() in der Gameloop kann sinnvoll sein.

    Man kann Updater und Gameloop auch zusammenlegen. Das macht man typischerweise so:

    Code:
    public void run() {
      while( running ) {
         long start = System.currentTimeMillis();
         .. neues bild aufbauen .. 
         long time = System.currentTimeMillis() - start;
         .. 1000 / FRAMERATE - time warten ..
        theView.postInvalidate();
      }
    
    Vielleicht ruckelt es aber auch nur deshalb, weil Du schlicht zu viel darstellen willst. :D

    ADD: Guck dir mal die Klasse SurfaceView an, die ist für asyncrones Malen von Google vorgesehen -
    habe selbst aber auch noch nicht verstanden wie genau, und ob das OffScreen rendering überflüssig macht.
     
    Zuletzt bearbeitet: 30.06.2011
    Duckemai bedankt sich.
  10. miha, 30.06.2011 #10
    miha

    miha Fortgeschrittenes Mitglied

    Beiträge:
    294
    Erhaltene Danke:
    43
    Registriert seit:
    12.01.2010
    Es wird einfach intern zwischen zwei Surfaces gewechselt (Surfaceflinger?). Einer wird gerade offscreen 'bemalt' und einer wird angezeigt. Double Buffering heisst das sonst überall. Dadurch spart man das eine Anzeigen der Offscreen-Bitmap und es dürfte ähnlich schnell sein wie vorher.
     
    Duckemai bedankt sich.
  11. Duckemai, 01.07.2011 #11
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    @miha:
    Heißt das, wenn ich die Klasse SurfaceView benutze (statt z.B. nur die View), dass dann schon automatisch das DoubleBuffering enthalten ist?

    @dieGoldeneMitte:
    Das ist nur eine Auflistung von mir. Hauptthread ist der Thread der das Spiel, also onDraw(), updatePhysics() usw sowie die GameLoop (also FPS usw) steuert. Dieser Hauptthread wird im
    Wechsel mit Deinem Thread durchlaufen. Und nein, ich habe keinen anderen Updater.
     
    Zuletzt bearbeitet: 01.07.2011
  12. miha, 01.07.2011 #12
    miha

    miha Fortgeschrittenes Mitglied

    Beiträge:
    294
    Erhaltene Danke:
    43
    Registriert seit:
    12.01.2010
    Ja. LunarLander ist da ein gutes Beispiel.
     
    Duckemai und DieGoldeneMitte haben sich bedankt.
  13. Duckemai, 01.07.2011 #13
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Ah, ok. LunarLander kenne ich. Wusste nur nicht, dass damit auch schon das DoubleBuffering automatisch mit abgearbeitet wird. Dann liegt es wohl doch an meinem Thread.sleep()...

    Vielen Dank!
     
  14. wilco, 02.07.2011 #14
    wilco

    wilco Android-Hilfe.de Mitglied

    Beiträge:
    106
    Erhaltene Danke:
    32
    Registriert seit:
    10.08.2010
    Wichtig zu beachten dabei ist dass die SurfaceView nach der Darstellung der Grafik die Bitmap immer komplett löscht. Du kannst damit also kein inkrementelles Rendering machen, sondern musst Deine Grafik immer vollständig neu berechnen.

    Um das zu umgehen musst Du Dich um's Double-Buffering dann doch wieder selbst kümmern, d.h. Grafik in eine eigene Bitmap rendern und diese dann in die SurfaceView en Block reinkopieren.

    Generell hilft Dir DoubleBuffering nur beim Flackern. Beim Ruckeln hast Du ein anderes Problem. Ohne Deinen Code zu kennen würde ich auf Gargabe Collection tippen. Schau mal im log ob während Dein Spiel läuft eine Garbage Collection angestossen wird.
     
    Duckemai bedankt sich.
  15. Duckemai, 02.07.2011 #15
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Ein sicherlich lohnenswerter Hinweis von Dir! Habe mal während das Spiel lief den Debugger laufen lassen und mir in der LogCat angeschaut, was da so alles abläuft. Der GC war auch mit dabei. Aber auch jede Menge anderer Sachen.
    Kein Wunder, dass das Spiel ruckelt wie Sau. Da wundert es mich eher, dass es so viele ruckelfreie Spiele gibt, und was die angestellt haben, damit es ruckelfrei wird. Denn die Hintergrundprozesse können die ja auch nicht einfach ausschalten.

    Dein Eintrag klang so, als sollte der GC nicht laufen. Aber das lässt sich doch in Java gar nicht verhindern. Oder?
     
  16. DieGoldeneMitte, 02.07.2011 #16
    DieGoldeneMitte

    DieGoldeneMitte Android-Lexikon

    Beiträge:
    1,230
    Erhaltene Danke:
    256
    Registriert seit:
    05.02.2010
    Phone:
    Nexus 5X
    Tablet:
    Nexus 7 (2013)
    Aber man kann durch geeignete Programmierung dafür sorgen, dass er möglichst wenig zu tun hat.
    Wenig temporäre Objekte erzeugen, Daten geeignet cachen, das Flyweight Pattern benutzen, etc etc ...
     
    Duckemai bedankt sich.
  17. wilco, 02.07.2011 #17
    wilco

    wilco Android-Hilfe.de Mitglied

    Beiträge:
    106
    Erhaltene Danke:
    32
    Registriert seit:
    10.08.2010
    Verhindern nicht, aber Du kannst einigermassen steuern wann die GC läuft.

    Dazu kannst Du die GC manuell anstossen zu Zeitpunkten wo sie Deinem Spielfluss nicht schadet: System.gc(). Am wichtigsten ist es allerdings dass Du selbst die GC nicht beschäftigst. D.h. in Deiner Main-Loop keine Variablen, Speicherblöcke etc. allokieren. Alle Variablen die Du dort verwendest sollten für die Klasse globale Variablen sein, d.h. nicht in einer Methode, und schon gar nicht in Deiner Draw Methode erstellt werden.

    Zwei Integer Variablen sind 64 Bit. Das ist nicht viel. Wenn Du die beiden bei jedem Durchlauf Deiner Draw Methode neu erstellst, sind das allerdings 64 Bit Speicher pro Durchlauf die reserviert werden. Wenn Dein Spiel mit 30 fps läuft sind dass dann an die 2k Speicher pro Sekunde.

    Sobald Dein Spiel 5 Minuten läuft macht das schon 600k die durch die garbage collection nur durch diese beiden Variablen aufgeräumt werden müssen. Jetzt kannst Du Dir ausrechnen wie das aussieht wenn Du ganze Grafiken allokierst. Da muss die GC dann fast jede Sekunde laufen. Und wenn die Garbage Collection läuft stockt erst mal alles.

    Wenn man konsequent in den Spiel-Schleifen auf jegliche Speicherallokation verzichtet und sicherheitshalber die GC noch ab und an selbst anstösst wo es nicht weh tut ruckelt auch nichts.
     
    Duckemai bedankt sich.
  18. Duckemai, 02.07.2011 #18
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Hmh! Das klingt doch irgendwie machbar. Vielleicht nicht optimiert wie ein Vollprofi. Aber wenn ich mir einige meiner verschachtelten Schleifen anschaue, dann könnte ich da schon was optimieren.

    Schmeiße dabei gleich mal alle Deklarationen in den Schleifen raus und bringe
    sie als Klassenvariablen unter, damit sie nur einmal deklariert werden müssen.

    Und dann noch das System.gc...

    Ich check mal alles bei mir durch und gebe morgen noch mal ein kurzes Feedback.

    Vielen Dank!
     
    Zuletzt bearbeitet: 02.07.2011
  19. Duckemai, 03.07.2011 #19
    Duckemai

    Duckemai Threadstarter Erfahrener Benutzer

    Beiträge:
    227
    Erhaltene Danke:
    6
    Registriert seit:
    05.04.2010
    Phone:
    Nexus
    Doch! Ich muss sagen, die Bitmaps laufen schon wesentlich ruhiger und das Ruckeln ist fast raus.

    Gruß an alle!
     

Diese Seite empfehlen