Taking screenshot programmatically doesnt capture the contents of surfaceVIew

17,337

Solution 1

I finally solved this . Below i give some code for anyone who wants to know how to take screenshots of a layout ,pictures from the camera without intent, screenshots(sort of) of the content of a surfaceView and save the screen shot in a folder :

public class Cam_View extends Activity implements SurfaceHolder.Callback {

    protected static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 0;
    private SurfaceView SurView;
    private SurfaceHolder camHolder;
    private boolean previewRunning;
    final Context context = this;
    public static Camera camera = null;
    private RelativeLayout CamView;
    private Bitmap inputBMP = null, bmp, bmp1;
    private ImageView mImage;

    @SuppressWarnings("deprecation")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera);

        CamView = (RelativeLayout) findViewById(R.id.camview);//RELATIVELAYOUT OR 
                                                              //ANY LAYOUT OF YOUR XML

        SurView = (SurfaceView)findViewById(R.id.sview);//SURFACEVIEW FOR THE PREVIEW 
                                                        //OF THE CAMERA FEED
        camHolder = SurView.getHolder();                           //NEEDED FOR THE PREVIEW
        camHolder.addCallback(this);                               //NEEDED FOR THE PREVIEW
        camHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//NEEDED FOR THE PREVIEW
        camera_image = (ImageView) findViewById(R.id.camera_image);//NEEDED FOR THE PREVIEW

        Button btn = (Button) findViewById(R.id.button1); //THE BUTTON FOR TAKING PICTURE

        btn.setOnClickListener(new OnClickListener() {    //THE BUTTON CODE
            public void onClick(View v) {
                  camera.takePicture(null, null, mPicture);//TAKING THE PICTURE
                                                         //THE mPicture IS CALLED 
                                                         //WHICH IS THE LAST METHOD(SEE BELOW)
            }
        });
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,//NEEDED FOR THE PREVIEW
        int height) {
        if(previewRunning) {
            camera.stopPreview();
        }
        Camera.Parameters camParams = camera.getParameters();
        Camera.Size size = camParams.getSupportedPreviewSizes().get(0);
        camParams.setPreviewSize(size.width, size.height);
        camera.setParameters(camParams);
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
            previewRunning=true;
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {                  //NEEDED FOR THE PREVIEW
        try {
            camera=Camera.open();
        } catch(Exception e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {             //NEEDED FOR THE PREVIEW
        camera.stopPreview();
        camera.release();
        camera=null;
    }

    public void TakeScreenshot(){    //THIS METHOD TAKES A SCREENSHOT AND SAVES IT AS .jpg
        Random num = new Random();
        int nu=num.nextInt(1000); //PRODUCING A RANDOM NUMBER FOR FILE NAME
        CamView.setDrawingCacheEnabled(true); //CamView OR THE NAME OF YOUR LAYOUR
        CamView.buildDrawingCache(true);
        Bitmap bmp = Bitmap.createBitmap(CamView.getDrawingCache());
        CamView.setDrawingCacheEnabled(false); // clear drawing cache
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
        bmp.compress(CompressFormat.JPEG, 100, bos); 
        byte[] bitmapdata = bos.toByteArray();
        ByteArrayInputStream fis = new ByteArrayInputStream(bitmapdata);

        String picId=String.valueOf(nu);
        String myfile="Ghost"+picId+".jpeg";

        File dir_image = new  File(Environment.getExternalStorageDirectory()+//<---
                        File.separator+"Ultimate Entity Detector");          //<---
        dir_image.mkdirs();                                                  //<---
        //^IN THESE 3 LINES YOU SET THE FOLDER PATH/NAME . HERE I CHOOSE TO SAVE
        //THE FILE IN THE SD CARD IN THE FOLDER "Ultimate Entity Detector"

        try {
            File tmpFile = new File(dir_image,myfile); 
            FileOutputStream fos = new FileOutputStream(tmpFile);

            byte[] buf = new byte[1024];
            int len;
            while ((len = fis.read(buf)) > 0) {
                fos.write(buf, 0, len);
            }
            fis.close();
            fos.close();
            Toast.makeText(getApplicationContext(),
                           "The file is saved at :SD/Ultimate Entity Detector",Toast.LENGTH_LONG).show();
            bmp1 = null;
            camera_image.setImageBitmap(bmp1); //RESETING THE PREVIEW
            camera.startPreview();             //RESETING THE PREVIEW
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private PictureCallback mPicture = new PictureCallback() {   //THIS METHOD AND THE METHOD BELOW
                                 //CONVERT THE CAPTURED IMAGE IN A JPG FILE AND SAVE IT

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {

            File dir_image2 = new  File(Environment.getExternalStorageDirectory()+
                            File.separator+"Ultimate Entity Detector");
            dir_image2.mkdirs();  //AGAIN CHOOSING FOLDER FOR THE PICTURE(WHICH IS LIKE A SURFACEVIEW
                                  //SCREENSHOT)

            File tmpFile = new File(dir_image2,"TempGhost.jpg"); //MAKING A FILE IN THE PATH
                            //dir_image2(SEE RIGHT ABOVE) AND NAMING IT "TempGhost.jpg" OR ANYTHING ELSE
            try { //SAVING
                FileOutputStream fos = new FileOutputStream(tmpFile);
                fos.write(data);
                fos.close();
                //grabImage();
            } catch (FileNotFoundException e) {
                Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
            }

            String path = (Environment.getExternalStorageDirectory()+
                            File.separator+"Ultimate EntityDetector"+
                                                File.separator+"TempGhost.jpg");//<---

            BitmapFactory.Options options = new BitmapFactory.Options();//<---
                options.inPreferredConfig = Bitmap.Config.ARGB_8888;//<---
            bmp1 = BitmapFactory.decodeFile(path, options);//<---     *********(SEE BELOW)
            //THE LINES ABOVE READ THE FILE WE SAVED BEFORE AND CONVERT IT INTO A BitMap
            camera_image.setImageBitmap(bmp1); //SETTING THE BitMap AS IMAGE IN AN IMAGEVIEW(SOMETHING
                                        //LIKE A BACKGROUNG FOR THE LAYOUT)

            tmpFile.delete();
            TakeScreenshot();//CALLING THIS METHOD TO TAKE A SCREENSHOT
            //********* THAT LINE MIGHT CAUSE A CRASH ON SOME PHONES (LIKE XPERIA T)<----(SEE HERE)
            //IF THAT HAPPENDS USE THE LINE "bmp1 =decodeFile(tmpFile);" WITH THE METHOD BELOW

        }
    };

    public Bitmap decodeFile(File f) {  //FUNCTION BY Arshad Parwez
        Bitmap b = null;
        try {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;

            FileInputStream fis = new FileInputStream(f);
            BitmapFactory.decodeStream(fis, null, o);
            fis.close();
            int IMAGE_MAX_SIZE = 1000;
            int scale = 1;
            if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
                scale = (int) Math.pow(
                        2,
                        (int) Math.round(Math.log(IMAGE_MAX_SIZE
                                / (double) Math.max(o.outHeight, o.outWidth))
                                / Math.log(0.5)));
            }

            // Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            fis = new FileInputStream(f);
            b = BitmapFactory.decodeStream(fis, null, o2);
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return b;
    }
}

If you want to take a simple screenshot(no camera feed is needed) the you can use the TakeScreenshot method alone.

If you want to take a screenshot of a surfaceView with is not possible to do from the surfaceview directly the use the mPicture, set the picture you capture as backgroung , and then call TakeScreenshot to take your screenshot.(as seen above)

If you want to take a picture with the camera without calling an other camera app with an intent the use the takePicture with the mPicture and the surfaceView stuff from the code above.

What the previous code does if used "as is" is to take a screenshot of the layout contents(buttons,imageviews etc) and set as backgroung an image from the camera.

Below i also provide a basic layout xml for the previous code :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"   
android:id="@+id/camview">

<SurfaceView
    android:id="@+id/sview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" />

<ImageView
    android:id="@+id/camera_image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:contentDescription="@string/app_name" />



<Button
    android:id="@+id/button1"
    style="?android:attr/buttonStyleSmall"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true" />


</RelativeLayout>

Dont forget to import what needs to be imported

Solution 2

According to the comment (https://groups.google.com/forum/#!topic/android-developers/jYjvm7ItpXQ) by an Android Framework engineer, SurfaceView isn't treated like a normal view:

The surface view is actually BEHIND your window, and a hole punched in the window for you to see it. You thus can put things on top of it in your window, but nothing in your window can appear behind it.

Since it's "BEHIND" the app window, consequently the drawing for the window's view wouldn't contain it. I suppose the only way then is to provide a method in your SurfaceView class to draw its own content to the provided Canvas/Bitmap.

Share:
17,337

Related videos on Youtube

mremremre1
Author by

mremremre1

https://play.google.com/store/apps/developer?id=MRE+Programming

Updated on September 15, 2022

Comments

  • mremremre1
    mremremre1 over 1 year

    I have an app that I want to be able to capture a screenshot. The background of the layout is a surfaceView that shows video from the rear camera. The following code is able to take a screenshot but the contents of the surfaceView is saved as black. Here is the code :

    btn.setOnClickListener(new OnClickListener()
    {
    
    public void onClick(View v)
    {
        Random num = new Random();
        int nu=num.nextInt(1000);
        Bitmap bmp;
        CamView.setDrawingCacheEnabled(true); 
        CamView.buildDrawingCache(true);
        Bitmap bmp2 = Bitmap.createBitmap(CamView.getDrawingCache()); //Screenshot of the layout
        CamView.setDrawingCacheEnabled(false);
    
        SurView.setDrawingCacheEnabled(true); 
        SurView.buildDrawingCache(true);
        Bitmap bmp1 = Bitmap.createBitmap(SurView.getDrawingCache()); //Screenshot of the surfaceView
        SurView.setDrawingCacheEnabled(false);
    
        Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(),bmp1.getConfig());
        Canvas canvas = new Canvas(bmOverlay); //Overlaying the 2 bitmaps
        canvas.drawBitmap(bmp1, 0,0, null);
        canvas.drawBitmap(bmp2, 0,0, null);
        bmp=bmOverlay;
    
        //saving the file
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
        bmp.compress(CompressFormat.JPEG, 100, bos); 
        byte[] bitmapdata = bos.toByteArray();
        ByteArrayInputStream fis = new ByteArrayInputStream(bitmapdata);
    
        String picId=String.valueOf(nu);
        String myfile="Ghost"+picId+".jpeg";
    
        File dir_image = new  File(Environment.getExternalStorageDirectory()+
                File.separator+"Ultimate Entity Detector");
        dir_image.mkdirs();
    
        try {
            File tmpFile = new File(dir_image,myfile); 
            FileOutputStream fos = new FileOutputStream(tmpFile);
    
             byte[] buf = new byte[1024];
                int len;
                while ((len = fis.read(buf)) > 0) {
                    fos.write(buf, 0, len);
                }
                    fis.close();
                    fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    });
    

    I updated the code. Now i create 2 bitmaps, 1 for the layout xml and 1 for the surfaceView and then i overlay them into a single bitmap. But the surfaceView bitmap is still black

  • Alex Cohn
    Alex Cohn over 10 years
    I believe that taking a picture is an overshoot here. You already have a camera open, and a preview surface. It's enough to ask for preview callback (in your case, that may be setOneShotPreviewCallback()), and convert the resulting YUV to bitmap. This should be much faster (less delay) than Capture.
  • Sagar Zala
    Sagar Zala over 9 years
    Hello @mremremre1, your code is working, but the image is rotated, why this happen?
  • mremremre1
    mremremre1 over 9 years
    @SagarZala try locking the activity in landscape . Add this to your manifest inside the <activity/> android:screenOrientation="landscape"
  • Anand Singh
    Anand Singh almost 9 years
    @mremremre1 i have implemented your answer in my project. But I'm getting black screen. Please help.
  • mremremre1
    mremremre1 almost 9 years
    @AnandSingh have you added the camera permission on the manifest ?
  • mremremre1
    mremremre1 almost 9 years
    @AnandSingh Great. Good luck with your app
  • Pir Fahim Shah
    Pir Fahim Shah over 8 years
    Please make simple the code of OnTakePicture() Function. Its a great answer the logic u used.
  • Ricky Patel
    Ricky Patel over 6 years
    i have implemented your answer in my project. But I'm getting black screen. Please help. – @AnandSingh ,@mremremre1
  • mohamad ali gharat
    mohamad ali gharat about 6 years