Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a SurfaceView larger than the screen?

I would like to effectively make a simple digital zoom for the camera preview, so I thought I would simply resize my SurfaceView to be larger than the screen. Other questions (such as 3813049) seem to indicate that this is easy, so I created the sample code below which I expect to let me see only half of the image horizontally (since the SurfaceView is twice as wide as the screen) and have the image only take up half of the screen horizontally. However, running it (when targeted to SDK version 4 on my Thunderbolt with Android 2.2.1) results in being able to see the whole image horizontally while filling the screen horizontally. The SurfaceView appears to behave as intended vertically (when I make it smaller than the screen), but Android won't allow me to make the SurfaceView larger than the screen.

How can I implement a digital zoom? (No, I cannot use Camera.Parameters.setZoom; not only is this not supported by Android 1.6, but different cameras support and implement this differently)

public class MagnifyTestActivity extends Activity implements SurfaceHolder.Callback {
    private MagnificationView mPreview;
    private SurfaceHolder mHolder;
    private Camera mCamera = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPreview = new MagnificationView(this);
        setContentView(mPreview);
        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public class MagnificationView extends SurfaceView {
        public MagnificationView(Context context) {
            super(context);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Display display = getWindowManager().getDefaultDisplay();
            int width = display.getWidth()*2;
            int height = display.getHeight()/2;
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = Camera.open();
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        mHolder.setFixedSize(w, h);
        mCamera.startPreview();
    }
}

UPDATE: Based on @Pulkit Sethi's response, it is possible to stretch/magnify the SurfaceView vertically, just not horizontally. To magnify the SurfaceView vertically, simply replace display.getHeight()/2 with display.getHeight()*2 above. Also observe that changing the width doesn't produce any horizontal magnification, either in my code or in Pulkit's.

like image 461
Ben Avatar asked May 18 '11 20:05

Ben


2 Answers

//Activity class

public class CameraActivity extends Activity implements SurfaceListener {

    private static final String TAG = "CameraActivity";

    Camera mCamera;
    CameraPreview mPreview;
    private FrameLayout mCameraPreview;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setContentView(R.layout.activity_camera);

        mCamera = getCameraInstance();
        mPreview = new CameraPreview(this, mCamera);

        mCameraPreview = (FrameLayout) findViewById(R.id.camera_preview);
        mCameraPreview.addView(mPreview);


    }

    @Override
    protected void onPause() {
        super.onPause();

        releaseCamera();
    }

    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = Camera.open();
        } catch (Exception e) {

        }
        return camera;
    }

    private void releaseCamera() {
        if (null != mCamera) {
            mCamera.release();
        }

        mCamera = null;
    }

    @Override
    public void surfaceCreated() {

        //Change these mate
        int width = 1000;
        int height = 1000;
        // Set parent window params
        getWindow().setLayout(width, height);

        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                width, height);
        mCameraPreview.setLayoutParams(params);
        mCameraPreview.requestLayout();
    }
}

// Preview class

public class CameraPreview extends SurfaceView implements
        SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";

    Context mContext;
    Camera mCamera;
    SurfaceHolder mHolder;

    public interface SurfaceListener{
        public void surfaceCreated();
    }
    SurfaceListener listener;


    public CameraPreview(Context context, Camera camera) {
        super(context);

        mContext = context;
        listener = (SurfaceListener)mContext;
        mCamera = camera;

        mHolder = getHolder();
        mHolder.addCallback(this);

        // Required prior 3.0 HC
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        try {
            mCamera.setPreviewDisplay(holder);

            Parameters params = mCamera.getParameters();
            //Change parameters here
            mCamera.setParameters(params);

            mCamera.startPreview();

            listener.surfaceCreated();

        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        Log.i(TAG, "Surface changed called");
        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        mCamera.setDisplayOrientation(90);

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

}

//Layout file 
<?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" >

    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="300dp"
        android:layout_height="400dp"
        android:layout_centerHorizontal="true"
        android:paddingTop="10dp" >
    </FrameLayout>

</RelativeLayout>
like image 157
Pulkit Sethi Avatar answered Nov 11 '22 02:11

Pulkit Sethi


You can't make your surfaceView bigger than the screen. That being said there are ways around it.

I found you can adjust the size of the canvas in the SurfaceView, which will allow zooming.

public class DrawingThread extends Thread {
private MagnificationView mainPanel;
private SurfaceHolder surfaceHolder;
private boolean run;

public DrawingThread(SurfaceHolder surface, MagnificationView panel){
    surfaceHolder = surface;
    mainPanel = panel;
}

public SurfaceHolder getSurfaceHolder(){
    return surfaceHolder;
}

public void setRunning (boolean run){
    this.run = run;
}

public void run(){
    Canvas c;
    while (run){
        c = null;
        try {
            c = surfaceHolder.lockCanvas(null);
            synchronized (surfaceHolder){
                mainPanel.OnDraw(c);
            }
        } finally {
            if (c != null){
                surfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

}

In the MagnificationView class add a method:

public void OnDraw(Canvas canvas){
    if (canvas!=null){
        canvas.save();
        canvas.scale(scaleX,scaleY);      

        canvas.restore();
    }
}

DrawingThread would be a thread you start in in your Activity. Also in your MagnificationView class override the OnTouchEvent to handle your own pinch-zoom (which will modify scaleX & scaleY.

Hope This solves your issue

like image 29
sgbel2 Avatar answered Nov 11 '22 03:11

sgbel2