Das Canvas einer SurfaceView als Bild speichern - wie?

A

Antdoit

Neues Mitglied
1
Mein aktueller Versuch, das Canvas einer SurfaceView als Bild lokal auf dem Handy zu speichern sieht so aus:

Code:
    public void saveImage(){
        Canvas canv = this.dView.getHolder().lockCanvas();
        Bitmap bitmap = Bitmap.createBitmap(canv.getWidth(), canv.getHeight(), Bitmap.Config.ARGB_8888);
        this.dView.getHolder().unlockCanvasAndPost(canv);
        Canvas canvas = new Canvas(bitmap);
        canvas.setBitmap(bitmap);
        this.rend.canvas = canvas;
        canvas = this.rend.DisplayResult();
        //this.dView.draw(canvas);
        try{
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/Android/data" + getApplicationContext().getPackageName() + "/files");
            if(!mediaStorageDir.exists())
                Log.e("MKDIRS---->", String.valueOf(mediaStorageDir.mkdirs()));
            File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "AdelPath_RenderResult.png");

            FileOutputStream fos = new FileOutputStream(mediaFile);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
        } catch(Exception e){
            Log.e("Error --->", e.toString());
        }
    }

Wie ihr seht logge ich eigentlich alle Entscheidungen und Ereignisse mit. Wenn ich in meiner App auf den "Bild speichern" Button im Optionsmenü klicke, wird im Logcat keine Meldung angezeigt (bedeutet eigentlich, dass der Pfad vorhanden war und dass es keinen Fehler beim Speichern des FileOutputStreams gab). Leider finde ich dann im Windows Explorer auf dem Handy keine Datei die "AdelPath_RenderResult.png" heißt. Was mache ich hier falsch?

Vielleicht noch kurz zur Erklärung des Codes:

ich hol mir erstmal das aktuelle Canvas mit "lockCanvas()" damit ich die Bitmap mit der passenden Größe erstelle. Danach erstelle ich ein Canvas mit der Bitmap.
Code:
this.rend
ist die Renderer Klasse, mit
Code:
this.rend.canvas
weise ich dem Renderer das zuvor erstellte Canvas zu. Danach weiße ich mit
Code:
canvas = this.rend.DisplayResult()
dem zuvor erstellten canvas das Ergebnis des Renderers zu.

Habt ihr da noch Ideen, was ich falsch mache?
 
Hi Andoit,

ich versuche dir mal wieder zu helfen hoffe diesmal klappts beim ersten Mal ;)

Ok also laut Android developer doc muss das Bitmap dass ind en canvas setzt mit canvas.setBitmap(bitmap) ein "muable" also veränderbares bitmap sein.
Jedoch erschffast du mit createBitmap() ein "immutable" also unveränderbares Bitmap also bleibt der canvas ein "null"-objekt. (Ausserdem sehe ich gerade das auch new Canvas(bitmap) eine exception wirft da es ebenso ein mutable bitmap erwartet.) Und wo nichts auszugeben sit ist ncihts auszugeben logisch oder?:)

Ausserdem solltest du wann immer du einen cnavs lockst es in einem try catch block machen versuchs mal und gucxk ob er dir ne exception wirft oder ein error oder so.

Mach es einfach immer so
Code:
try
{
       SurfaceHolder.lockCanvas();
        //draw on canvas
}
catch(Throwable t)//oder error oder exception mit throwable fängst du aber alles ab
{
       // füttere den logcat
}

finally
{
      if(canvas!=null)
          SurfaceHolder.unlockCanvasAndPost(canvas);
}

so hier noch ein link falls dui wissen möchtest wie man aus einem immutable ein mutable bitmap macht achtung du musst dabei umwege gehen es scheint wohl nciht so einfach in 2,3 zeilen code zu gehen!!!

Und hier noch ein weiterer Link der ein bisschen mehr erklärt
 
Hi Jaiel,

danke für deine Antwort.

Laut der Android Developer Seite über die Bitmap-Klasse sollte das hier:

Code:
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

ja bereits eine mutable Bitmap erzeugen.

Mein jetziger Code sieht so aus:

Code:
    public void saveImage(){
        Canvas canv = null;
        try {
            canv = this.dView.getHolder().lockCanvas();
        } catch(Throwable t){
            Log.e("ERROR->", t.getMessage());
        } finally{
            if(canv != null)
                this.dView.getHolder().unlockCanvasAndPost(canv);
        }

        Bitmap bitmap = Bitmap.createBitmap(canv.getWidth(), canv.getHeight(), Bitmap.Config.ARGB_8888);
        bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

        Canvas canvas = new Canvas(bitmap);
        canvas.setBitmap(bitmap);

        this.rend.canvas = canvas;
        canvas = this.rend.DisplayResult();

        try{
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/Android/data" + getApplicationContext().getPackageName() + "/files");
            if(!mediaStorageDir.exists())
                Log.e("MKDIRS---->", String.valueOf(mediaStorageDir.mkdirs()));
            File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "AdelPath_RenderResult.png");

            FileOutputStream fos = new FileOutputStream(mediaFile);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
        } catch(Exception e){
            Log.e("Error --->", e.toString());
        }
    }

Jetzt kommt beim Drücken allerdings keine von mir abgefangene Fehlermeldung, sondern eine ganz andere:

12-18 15:09:01.968 24533-24533/com.example.standardbenutzer.adelpath A/libc Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 24533 (nutzer.adelpath)
12-18 15:09:01.968 24533-24533/com.example.standardbenutzer.adelpath A/libc Send stop signal to pid:24533 in void debuggerd_signal_handler(int, siginfo_t*, void*)
 
mmmmh ich hab mal gegoogelt und es sieht so aus als ob das bedeutet das du speciher abfragst der nicht initialisiert ist der dir nicht zu steht und er sendet einen stop was den prozess killt. ( wikipedia zu dem SIGSEGV

poste mal dein logcat bitte

ich hab mal schnell bei stackoverflow(ist ein bekanntes forum) gelesen dass das pasieren kann wenn man auf speciher zu greift der nciht initialisiert ist wie zum beispiel von einem canvas die width und height erfragen obwohl es auf "null" steht


mach mal den rest des codes auch in einen try block um mal zu guvcken und fang das ab. zum beispiel vermute ich heir dieses:

Bitmap bitmap = Bitmap.createBitmap(canv.getWidth(), canv.getHeight(), Bitmap.Config.ARGB_8888);

ich weiß ja nciht kann es sein das der zurückgelieferte canvas "null" vielleciht weil das surface noch nciht created wurde???
was irgendiwe keinen sinn ergibt!:D


btw.: sorry dass ich dir zu throwable rate es ist ein besserer stil so spezifisch wie möglich zu sein weil throwable kann amnchmal unerwartete bugs kreieren da es einfach alles von error bis runter zu den spezial exceptions abdeckt...irgendwie wird es als schlechter stil empfunden

P.S.: stimmt ich habe das mit bitmap.copy dannn übersehen sorry
und ich kann dich auf diese seite verweisen so wie es viel tun


gruß
 
Zuletzt bearbeitet:
poste mal dein logcat bitte

Die Fehlermeldungen waren das Logcat.

wie zum beispiel von einem canvas die width und height erfragen obwohl es auf "null" steht

Dann würde eine NullPointerException kommen. Kam aber nicht außerdem lief beim Debuggen auch alles einwandfrei durch.

ich weiß ja nciht kann es sein das der zurückgelieferte canvas "null" vielleciht weil das surface noch nciht created wurde???
was irgendiwe keinen sinn ergibt!

Dann würde das Debuggen nicht durchlaufen und es würde eine Exception kommen.

und ich kann dich auf diese seite verweisen so wie es viel tun

Danke für den Link ich hab da jetzt aber beim drüberlesen nicht wirklich was gefunden, was mir hier weiter hilft.

Ich hab jetzt noch mal einen Ansatz verfolgt, wo ich mein Bild direkt auf eine Bitmap mahle ohne erst ein Canvas zu erzeugen:

Code:
    public Bitmap GetBitmap(){
        int width = this.canvas.getWidth();
        int height = this.canvas.getHeight();

        Bitmap rBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        int i = 0;
        for(int x = 0; x < width; x++){
            for(int y = 0; y < height; y++){
                int r = (int)(accumulator[i*3+0] / (float)samples);
                int g = (int)(accumulator[i*3+1] / (float)samples);
                int b = (int)(accumulator[i*3+2] / (float)samples);

                rBitmap.setPixel(x,y,Color.rgb(r,g,b));
                i++;
            }
        }

        return rBitmap;
    }

Dann mach ich in der Speichern Funktion nur das hier:

Code:
    public void saveImage(){
        Bitmap bitmap = this.rend.GetBitmap();

        try{
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/Android/data" + getApplicationContext().getPackageName() + "/files");
            if(!mediaStorageDir.exists())
                Log.e("MKDIRS---->", String.valueOf(mediaStorageDir.mkdirs()));
            File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "AdelPath_RenderResult.png");

            FileOutputStream fos = new FileOutputStream(mediaFile);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
        } catch(Exception e){
            Log.e("Error --->", e.toString());
        }
    }

Jetzt kommt zwar keine Fehlermeldung mehr, ein Bild hab ich aber auch nicht :confused2:
 
Zuletzt bearbeitet:
Bittet haut mich nicht aber anscheinend zeigt der Windows Explorer nicht immer sofort alle Änderungen an. Wenn ich direkt auf dem Smartphone navigiere sehe ich auf einmal die Bilder (die meisten sind allerdings leer). :wubwub:

Mit der letzten Methode, wo ich direkt auf eine Bitmap zeichne, funktioniert es dann auch.
Wenn ihr Fehler bei weißen Pixeln im Bild hab, setzt am besten mal:

Code:
        Bitmap rBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        rBitmap.hasAlpha();

hat bei mir funktioniert.

Danke und schönen Abend noch :lol:
 

Ähnliche Themen

B
Antworten
6
Aufrufe
1.062
jogimuc
J
Manny87
  • Manny87
Antworten
11
Aufrufe
193
swa00
swa00
A
Antworten
10
Aufrufe
1.948
swa00
swa00
Zurück
Oben Unten