2

I have a canvas which I'm displaying a picture from the media store as its background and then I'm drawing on it. I'd like to save the background+what has been drawn over it but I'm only able to save the background, which is useless for me. I am trying to understand what I'm doing wrong. I basically did what people suggest here but it didn't help.

Here is the code which I have: EDIT: THIS TIME I UPLOADED ALL THE CODE IN HERE

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.view.Display;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Toast;

public class CanvasDrawingActivity extends  Activity   {
private ArrayList<Path> _graphics = new ArrayList<Path>();
private Paint mPaint;
public static final int GET_FROM_GALLERY = 1;
public static final int IMAGE_CAPTURE = 0;
public static final int SELECT_IMAGE_FROM_DEVICE = 1;
public Bitmap myBitmap;
public int isFirstTime = 0;
public DrawingPanel mPanel;
static int id = 1;
public Uri fileUri ;

@Override
public void onCreate(Bundle savedInstanceState) {
    mPanel = new DrawingPanel(this);
    mPanel.setId(findId());
    super.onCreate(savedInstanceState);
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //https://stackoverflow.com/questions/10937659/android-image-doesnt-save-using-native-camera-app-on-nexus-s
    File f = new File(Environment.getExternalStorageDirectory().getPath(), String.format("%d.jpg", System.currentTimeMillis()));
    fileUri= Uri.fromFile(f);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

    startActivityForResult(intent, IMAGE_CAPTURE);
    setContentView(R.layout.main);
}


public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.save:
        savePicture();
        return true;
    case R.id.clear:
        // startActivity(new Intent(this, Clear.class));
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

public void savePicture(){
    long now = System.currentTimeMillis();
    OutputStream fos;
    try {
        fos = new FileOutputStream(String.format(Environment.getExternalStorageDirectory().getAbsolutePath()+"/edited_%d.jpg",now));
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); 
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finish();  //close the activity. 
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    //Detects request codes
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Uri result; 
        result = fileUri;
        //refresh storage
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
        try {
            myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), result);
            startDrawing();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    else if (resultCode == Activity.RESULT_CANCELED){

    }
}

public void startDrawing(){
    setContentView(mPanel);
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setColor(0xFFFFFF00);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(3);
}

public int findId(){  
    View v = findViewById(id);  
    while (v != null){  
        v = findViewById(++id);  
    }  
    return id++;  
}


class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {
    private DrawingThread _thread;
    private Path path;

    public DrawingPanel(Context context) {
        super(context);
        setDrawingCacheEnabled(true);
        getHolder().addCallback(this);
        _thread = new DrawingThread(getHolder(), this);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        synchronized (_thread.getSurfaceHolder()) {
            if(event.getAction() == MotionEvent.ACTION_DOWN){
                path = new Path();
                path.moveTo(event.getX(), event.getY());
                path.lineTo(event.getX(), event.getY());
            }else if(event.getAction() == MotionEvent.ACTION_MOVE){
                path.lineTo(event.getX(), event.getY());
                if(_graphics.size() > 0) {
                    _graphics.remove(_graphics.size() - 1);
                }
                _graphics.add(path);
            }else if(event.getAction() == MotionEvent.ACTION_UP){
                path.lineTo(event.getX(), event.getY());
                _graphics.remove(_graphics.size() - 1);
                _graphics.add(path);
            }
            return true;
        }
    }

    //Source: http://thinkandroid.wordpress.com/2009/12/25/resizing-a-bitmap/
    public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // create a matrix for the manipulation
        Matrix matrix = new Matrix();
        // resize the bit map
        matrix.postScale(scaleWidth, scaleHeight);
        // recreate the new Bitmap
        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

    /**
     * Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
     * more memory that there is already allocated.
     * 
     * @param imgIn - Source image. It will be released, and should not be used more
     * @return a copy of imgIn, but muttable.
     */
    public Bitmap convertToMutable(Bitmap imgIn) {
        try {
            //this is the file going to use temporally to save the bytes. 
            // This file will not be a image, it will store the raw image data.
            File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");

            //Open an RandomAccessFile
            //Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
            //into AndroidManifest.xml file
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

            // get the width and height of the source bitmap.
            int width = imgIn.getWidth();
            int height = imgIn.getHeight();
            Config type = imgIn.getConfig();

            //Copy the byte to the file
            //Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
            FileChannel channel = randomAccessFile.getChannel();
            MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
            imgIn.copyPixelsToBuffer(map);
            //recycle the source bitmap, this will be no longer used.
            imgIn.recycle();
            System.gc();// try to force the bytes from the imgIn to be released

            //Create a new bitmap to load the bitmap again. Probably the memory will be available. 
            imgIn = Bitmap.createBitmap(width, height, type);
            map.position(0);
            //load it back from temporary 
            imgIn.copyPixelsFromBuffer(map);
            //close the temporary file and channel , then delete that also
            channel.close();
            randomAccessFile.close();

            // delete the temp file
            file.delete();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } 

        return imgIn;
    }


    @Override
    public void onDraw(Canvas canvas) {
        Display display = getWindowManager().getDefaultDisplay();
        int width = display.getWidth();
        int height = display.getHeight();
        myBitmap = getResizedBitmap(myBitmap, height, width);

        if (isFirstTime<3){
            myBitmap = convertToMutable(myBitmap);
            canvas.drawBitmap(myBitmap, 0, 0, null);
            isFirstTime++;
        }

        for (Path path : _graphics) {
            //canvas.drawPoint(graphic.x, graphic.y, mPaint);
            canvas.drawPath(path, mPaint);
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub

    }

    public void surfaceCreated(SurfaceHolder holder) {          
        // TODO Auto-generated method stub
        _thread.setRunning(true);
        _thread.start();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }
}

class DrawingThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private DrawingPanel _panel;
    private boolean _run = false;

    public DrawingThread(SurfaceHolder surfaceHolder, DrawingPanel panel) {
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }

    public void setRunning(boolean run) {
        _run = run;
    }

    public SurfaceHolder getSurfaceHolder() {
        return _surfaceHolder;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            c = null;
            try {
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    if (c!=null)
                        _panel.onDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

}

This is main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" 
android:id="@+id/main_view">
</LinearLayout>

and menu.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
    android:id="@+id/save"
    android:title="Save"/>
<item
    android:id="@+id/clear"
    android:title="Clear"/>
</menu>

Any help would be appreciated. Thanks!!

EDIT: I got it to work!! Here it is!

@Override
public void onCreate(Bundle savedInstanceState) {
    mPanel = new DrawingPanel(this);
    mPanel.setId(findId());
    super.onCreate(savedInstanceState);
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //https://stackoverflow.com/questions/10937659/android-image-doesnt-save-using-native-camera-app-on-nexus-s
    File f = new File(Environment.getExternalStorageDirectory().getPath(), String.format("%d.jpg", System.currentTimeMillis()));
    fileUri= Uri.fromFile(f);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

    startActivityForResult(intent, IMAGE_CAPTURE);
    setContentView(R.layout.main);
}


public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case R.id.save:
        savePicture();
        return true;
    case R.id.clear:
        // startActivity(new Intent(this, Clear.class));
        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
}

public void savePicture(){
    long now = System.currentTimeMillis();
    OutputStream fos;
    try {
        //SAVE THIS ONE- replicate all paths
        Canvas canvas = new Canvas(myBitmap);
        for (Path path : _graphicsToSave)
        {
            canvas.drawPath(path, mPaint);
        }

        fos = new FileOutputStream(String.format(Environment.getExternalStorageDirectory().getAbsolutePath()+"/edited_%d.jpg",now));
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); 
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finish();  //close the activity. 
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    //Detects request codes
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == IMAGE_CAPTURE && resultCode == RESULT_OK) {
        Uri result; 
        result = fileUri;
        //refresh storage
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
        try {
            myBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), result);

            //resize the picture to match the screen size
            Display display = getWindowManager().getDefaultDisplay();
            int width = display.getWidth();
            int height = display.getHeight();
            myBitmap = getResizedBitmap(myBitmap, height, width);
            startDrawing();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    else if (resultCode == Activity.RESULT_CANCELED){

    }
}

//Source: http://thinkandroid.wordpress.com/2009/12/25/resizing-a-bitmap/
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
    int width = bm.getWidth();
    int height = bm.getHeight();
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    // create a matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleWidth, scaleHeight);
    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
    return resizedBitmap;
}


public void startDrawing(){
    setContentView(mPanel);
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setColor(0xFFFFFF00);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(3);
}

public int findId(){  
    View v = findViewById(id);  
    while (v != null){  
        v = findViewById(++id);  
    }  
    return id++;  
}


class DrawingPanel extends SurfaceView implements SurfaceHolder.Callback {
    private DrawingThread _thread;
    private Path path;

    public DrawingPanel(Context context) {
        super(context);
        setDrawingCacheEnabled(true);
        getHolder().addCallback(this);
        _thread = new DrawingThread(getHolder(), this);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        synchronized (_thread.getSurfaceHolder()) {
            if(event.getAction() == MotionEvent.ACTION_DOWN){
                path = new Path();
                path.moveTo(event.getX(), event.getY());
                path.lineTo(event.getX(), event.getY());
            }else if(event.getAction() == MotionEvent.ACTION_MOVE){
                path.lineTo(event.getX(), event.getY());
                if(_graphics.size() > 0) {
                    _graphics.remove(_graphics.size() - 1);
                }
                _graphics.add(path);
            }else if(event.getAction() == MotionEvent.ACTION_UP){
                path.lineTo(event.getX(), event.getY());
                _graphics.remove(_graphics.size() - 1);
                _graphics.add(path);
                _graphicsToSave.add(path);
            }
            return true;
        }
    }




    @Override
    public void onDraw(Canvas canvas) {


        if (isFirstTime<3){
            canvas.drawBitmap(myBitmap, 0, 0, null);
            isFirstTime++;
        }

        for (Path path : _graphics) {
            canvas.drawPath(path, mPaint);
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub

    }

    public void surfaceCreated(SurfaceHolder holder) {            
        // TODO Auto-generated method stub
        _thread.setRunning(true);
        _thread.start();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }
}

class DrawingThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private DrawingPanel _panel;
    private boolean _run = false;

    public DrawingThread(SurfaceHolder surfaceHolder, DrawingPanel panel) {
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }

    public void setRunning(boolean run) {
        _run = run;
    }

    public SurfaceHolder getSurfaceHolder() {
        return _surfaceHolder;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            c = null;
            try {
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    if (c!=null)
                        _panel.onDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

}

Community
  • 1
  • 1
mila
  • 471
  • 8
  • 20

2 Answers2

0
View mView;
mView.setDrawingCacheEnabled(true);

Bitmap newimg = mView.getDrawingCache();

Please get a bitmap and try saving that bitmap. Let me know you need more help

  • What View are you referring to? I have LinearLayout and an instance of a SurfaceView. Thanks. – mila Sep 21 '12 at 14:58
0

To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).

Your current pasted code code shows that you are saving the myBitmap but it doesn't show whether you are setting the myBitmap as the bitmap for drawing or not. May be it's not the complete code.
The only reason i could think of for this problem is that you are not drawing on the bitmap that you are saving. Here is a link to the code I created for a finger paint. You can refer to this code to see how the bitmap is provided to the canvas and how path is drawn on the bitmap.

karn
  • 5,963
  • 3
  • 22
  • 29
  • I'm unable to run the code from the link: 09-23 18:38:33.843: E/AndroidRuntime(12056): FATAL EXCEPTION: main 09-23 18:38:33.843: E/AndroidRuntime(12056): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.canvasexample/com.example.canvasexample.MainActivity}: java.lang.InstantiationException: can't instantiate class com.example.canvasexample.MainActivity; no empty constructor – mila Sep 24 '12 at 01:44
  • I have uploaded the whole project here https://www.dropbox.com/sh/271mn739b6hbjjz/1_NvXqWyM1 – karn Sep 25 '12 at 09:47