基于GLSurfaceView实现自定义Camera

Android中的Camera简介

这里主要将一些camera的一些简单用法,当然如果是用camera2的童鞋,可以参考:http://blog.csdn.net/vinicolor/article/details/50992692 。这里主要讲解:
* 如何利用camera开启相机实现预览
* 实现拍照功能,以及图片大小设定
* 切换前后置摄像头
* 闪光灯的开启
* 前置镜像
* 给拍照的图片加水印
* 自动对焦与手动对焦
* 相机的放大缩小
* 相机拍照的横竖方向的监听
好了,我们先来先看看预览的效果
这里写图片描述

在之前,我先定义了一个接口ICameraHelper,用于控制整个相机的功能:

/**
 * @CreadBy :DramaScript
 * @date 2017/7/10
 */
public interface ICameraHelper {

    //打开相机
    boolean open(int cameraId);
    //设置相机宽高比  图片和预览大小
    void setConfig(Config config);
    //是否开启预览
    boolean preview();
    // 切换前后摄像头
    boolean switchTo(int cameraId);
    // 拍照
    void takePhoto(TakePhotoCallback callback,int nRotation);
    // 相机是否关闭
    boolean close();
    //设置预览的纹理
    void setPreviewTexture(SurfaceTexture texture);
    //获得预览大小
    Point getPreviewSize();
    //获得图片的大小
    Point getPictureSize();
    //设置预览时每一帧的回调
    void setOnPreviewFrameCallback(PreviewFrameCallback callback);

    void setFlash(int flashMode);

    class Config{
        float rate; //宽高比
        int minPreviewWidth;
        int minPictureWidth;
    }

    interface TakePhotoCallback{
        void onTakePhoto(Bitmap bitmap);
    }

    interface PreviewFrameCallback{
        void onPreviewFrame(byte[] bytes, int width, int height);
    }
}

下面我们定义CameraHelper实现ICameraHelper,然后来实现这些方法:

首先实现打开相机方法open(int cameraId):

 @Override
    public boolean open(int cameraId) {
        mCamera = Camera.open(cameraId);
        this.cameraId = cameraId;
        if (mCamera != null) {
            Camera.Parameters param = mCamera.getParameters();
            picSize = getPropPictureSize(param.getSupportedPictureSizes(), mConfig.rate,
                    mConfig.minPictureWidth);
            preSize = getPropPreviewSize(param.getSupportedPreviewSizes(), mConfig.rate, mConfig
                    .minPreviewWidth);
            //设置照片生成的大小
            picWide = picSize.width;
            picHeight = picSize.height;
            param.setPictureSize(picWide, picHeight);
            //生成照片预览大小
            param.setPreviewSize(preSize.width, preSize.height);
            mCamera.setParameters(param);
            Camera.Size pre = param.getPreviewSize();
            Camera.Size pic = param.getPictureSize();
            mPicSize = new Point(pic.height, pic.width);
            mPreSize = new Point(pre.height, pre.width);
            Log.e("wuwang", "camera previewSize:" + mPreSize.x + "/" + mPreSize.y);

            return true;
        }
        return false;
    }

 /**
     * 获得生成照片的大小
     *
     * @param list
     * @param th
     * @param minWidth
     * @return
     */
    private Camera.Size getPropPictureSize(List<Camera.Size> list, float th, int minWidth) {
        Collections.sort(list, sizeComparator);

        int i = 0;
        for (Camera.Size s : list) {
            if ((s.height >= minWidth) && equalRate(s, th)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;
        }
        return list.get(i);

 /**
     * 获得预览大小
     *
     * @param list
     * @param th
     * @param minWidth
     * @return
     */
    private Camera.Size getPropPreviewSize(List<Camera.Size> list, float th, int minWidth) {
        Collections.sort(list, sizeComparator);

        int i = 0;
        for (Camera.Size s : list) {
            if ((s.height >= minWidth) && equalRate(s, th)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;
        }
        return list.get(i);
    }

这个方法,通过Camera.open(cameraId);来获取Camera对象,其中cameraId可以为0也可以为1,前者代表后置摄像头。然后通过Camera.Parameters来设置相机的一些参数,比如预览大小,图片生成大小,当然这里使根据手机找出了最合适的尺寸大小,可以根据自己的需求设定图片的大小,至于预览的大小,建议不要太大,容易造成机型不适配,我记得HTC的U的手机,最大支持720的。设置太大也容易消化GPU,造成手机的发烫。

接下来就是拍照方法实现:

@Override
    public void takePhoto(final TakePhotoCallback callback, final int nRotation) {
        mCamera.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                Log.e("tag", "拍照的角度:" + nRotation);
                Bitmap bm = null;
                if (nRotation==0){
                    bm = setTakePicktrueOrientation(cameraId, BitmapFactory.decodeByteArray(data, 0, data.length));
                }else {
                    bm  = BitmapFactory.decodeByteArray(data, 0, data.length);
                    if (isFrontCamera) {
                        bm = rotateBitmapByDegree(bm, 270 - nRotation, false);
                    } else {
                        bm = rotateBitmapByDegree(bm, nRotation + 90, false);
                    }
                }
                callback.onTakePhoto(bm);
            }
        });
    }

这里拍照方法,调用takePicture方法,实现Camera.PictureCallback接口,为了后面对bitmap操作,直接将byte数组转成bitmap返回。这里有一个问题,那就是拍照的图片并不是拍照时的样子,这里我们通过:

 private class AlbumOrientationEventListener extends OrientationEventListener {
        public AlbumOrientationEventListener(Context context) {
            super(context);
        }

        public AlbumOrientationEventListener(Context context, int rate) {
            super(context, rate);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
                return;
            }

            //保证只返回四个方向
            int newOrientation = ((orientation + 45) / 90 * 90) % 360;
            if (newOrientation != mOrientation) {
                mOrientation = newOrientation;
                Log.e("MJHTEST", "mOrientation = " + mOrientation);
                //返回的mOrientation就是手机方向,为0°、90°、180°和270°中的一个
            }
        }
    }

来监听手机的横竖变化,根据相应的角度进行调整。下面你是矫正角度的方法:

 /**
     * 将图片按照某个角度进行旋转
     *
     * @param bm     需要旋转的图片
     * @param degree 旋转角度
     * @return 旋转后的图片
     */
    public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree, boolean bFlip) {

        if (degree >= 360)
            degree -= 360;
        if (degree < 0)
            degree += 360;
        Bitmap returnBm = null;

        // 根据旋转角度,生成旋转矩阵
        android.graphics.Matrix matrix = new android.graphics.Matrix();

        matrix.postRotate(degree);

        try {
            // 将原始图片按照旋转矩阵进行旋转,并得到新的图片
            returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
                    bm.getHeight(), matrix, true);
        } catch (OutOfMemoryError e) {
        }
        if (returnBm == null) {
            returnBm = bm;
        }
        if (bm != returnBm) {
            bm.recycle();
        }
        return returnBm;
    }

至于切换摄像头,其实就是根据cameraId的变化,重新开启预览相机:

 @Override
    public boolean switchTo(int cameraId) {
        close();
        open(cameraId);
        if (cameraId == 1) {
            isFrontCamera = true;
        } else {
            isFrontCamera = false;
        }
        return false;
    }

然后开启闪光灯的操作,有三种模式:开启常亮的闪光灯,拍照时根据环境亮度开启闪光灯,关闭闪光灯三种模式:

  /**
     * 开启闪光灯
     */
    public void turnLightOn() {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        // Check if camera flash exists
        if (flashModes == null) {
            // Use the screen as a flashlight (next best thing)
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_ON)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }

    //关闭闪光灯
    public void turnLightOff() {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        String flashMode = parameters.getFlashMode();
        // Check if camera flash exists
        if (flashModes == null) {
            return;
        }
        if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            // Turn off the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                mCamera.setParameters(parameters);
            } else {
            }
        }
    }

接下来就是前置摄像头镜像功能,意思就是我们的手机拍照,前置拍出来的照片都是左右反的,需要将图片进行相应的调整:

/**
     * 拍摄照片自动镜像
     * @param bmp
     * @return
     */
    public static Bitmap convertBmp(Bitmap bmp) {
        int w = bmp.getWidth();
        int h = bmp.getHeight();

        Matrix matrix = new Matrix();
        matrix.postScale(-1, 1); // 镜像水平翻转
        Bitmap convertBmp = Bitmap.createBitmap(bmp, 0, 0, w, h, matrix, true);

        return convertBmp;
    }

拍好照片,可能需要考虑给照片加个水印,这里不讨论实时的给相机每一帧加,因为没必要,又不是做贴纸,所以只是在拍照后加入水印操作:

 /**
     * 设置水印图片在右下角
     * @param src
     * @param watermark
     * @param paddingRight
     * @param paddingBottom
     * @return
     */
    public static Bitmap createWaterMaskRightBottom(
            Context context, Bitmap src, Bitmap watermark,
            int paddingRight, int paddingBottom) {
        return createWaterMaskBitmap(src, watermark,
                src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight),
                src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom));
    }

    private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark,
                                                int paddingLeft, int paddingTop) {
        if (src == null) {
            return null;
        }
        int width = src.getWidth();
        int height = src.getHeight();
        //创建一个bitmap
        Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
        //将该图片作为画布
        Canvas canvas = new Canvas(newb);
        //在画布 0,0坐标上开始绘制原始图片
        canvas.drawBitmap(src, 0, 0, null);
        //在画布上绘制水印图片
        canvas.drawBitmap(watermark, paddingLeft, paddingTop, null);
        // 保存
        canvas.save(Canvas.ALL_SAVE_FLAG);
        // 存储
        canvas.restore();
        return newb;
    }

每个相机基本上都有自动对焦和手动对焦功能,自动对焦其实就是在初始化相机的设置参数:

 camera.cancelAutoFocus();//只有加上了这一句,才会自动对焦

手动对焦,需要获取屏幕收触控的点,根据点来设置对焦的地方:

 /**
     * 手动聚焦
     *
     * @param point 触屏坐标
     */
    protected boolean onFocus(Point point, Camera.AutoFocusCallback callback) {
        mCamera = manager.getmCamera();
        if (mCamera == null) {
            Log.e("tag","------------------------1");
            return false;
        }

        Camera.Parameters parameters = null;
        try {
            parameters = mCamera.getParameters();
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("tag","------------------------2");
            return false;
        }
        //不支持设置自定义聚焦,则使用自动聚焦,返回

        if(Build.VERSION.SDK_INT >= 14) {

            if (parameters.getMaxNumFocusAreas() <= 0) {
                return focus(callback);
            }

            Log.i("tag", "onCameraFocus:" + point.x + "," + point.y);

            List<Camera.Area> areas = new ArrayList<Camera.Area>();
            int left = point.x - 300;
            int top = point.y - 300;
            int right = point.x + 300;
            int bottom = point.y + 300;
            left = left < -1000 ? -1000 : left;
            top = top < -1000 ? -1000 : top;
            right = right > 1000 ? 1000 : right;
            bottom = bottom > 1000 ? 1000 : bottom;
            areas.add(new Camera.Area(new Rect(left, top, right, bottom), 100));
            parameters.setFocusAreas(areas);
            try {
                //本人使用的小米手机在设置聚焦区域的时候经常会出异常,看日志发现是框架层的字符串转int的时候出错了,
                //目测是小米修改了框架层代码导致,在此try掉,对实际聚焦效果没影响
                mCamera.setParameters(parameters);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
                Log.e("tag","------------------------3");
                return false;
            }
        }


        return focus(callback);
    }

    private boolean focus(Camera.AutoFocusCallback callback) {
        try {
            mCamera.autoFocus(callback);
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("tag","------------------------4");
            return false;
        }
        return true;
    }

最后就是相机的缩放了,直接上代码了:

 public void setZoom(int zoom) {
        if (mCamera == null) return;
        Camera.Parameters parameters;
        //注意此处为录像模式下的setZoom方式。在Camera.unlock之后,调用getParameters方法会引起android框架底层的异常
        //stackoverflow上看到的解释是由于多线程同时访问Camera导致的冲突,所以在此使用录像前保存的mParameters。
        parameters = mCamera.getParameters();

        if (!parameters.isZoomSupported()) return;
        parameters.setZoom(zoom);
        mCamera.setParameters(parameters);
        mZoom = zoom;
    }

好了,相机camera的基本操作都在这里。

基于GlSurfaceView自定义CamerView自定义相机

我们相机的数据有两种常用的方法来预览,第一种就是利用SurfaceView控件,设置SurfaceHolder句柄来预览,这种方式相对比较简单,但是却不能很好的满足我们一些需求,比如在自定义相机中设置自定义的滤镜,贴纸,甚至加上AR的效果。所以,我们选择第二种方式,利用Opengl来绘制摄像头的数据。这样方便后面的拓展,其中的流程如下图:
这里写图片描述
这里面的关键是生成的纹理id如何通过opengl来绑定在一起,然后通过纹理id来生成SurfaceTexture,然后将摄像数据与GLSurfaceView绑定在一起显示出来。下面我们通过代码来分析。首先如何生成纹理id:

private int createTextureID(){
        int[] texture = new int[1];
        GLES20.glGenTextures(1, texture, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
        return texture[0];
    }

此时的纹理id是没有和opengl绑定相关的,这个时候需要在GLSurfaceView的Render中的onSurfaceCreated进行相应的绑定操作:

 @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        int texture = createTextureID();
        surfaceTexture=new SurfaceTexture(texture);
        mOesFilter.create();
        mOesFilter.setTextureId(texture);
    }

上面通过纹理id生成了一个SurfaceTexture对象,然后把这个对象交给相机的CameraHelper,通过一下方法设置:

@Override
    public void setPreviewTexture(SurfaceTexture texture) {
        if (mCamera != null) {
            try {
                mCamera.setPreviewTexture(texture);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

这是时候摄像头的数据就已经和GLSurfaceView完成一半的工作了,不了解GLSurfaceView的绘图方式的请参考:http://www.jianshu.com/p/442682fda917
接下来,就是需要通过opengl的一些操作将摄像头数据正常显示出来了,其实主要还是实现onDrawFrame方法:

/**
     * 启用顶点坐标和纹理坐标进行绘制
     */
    protected void onDraw(){
        GLES20.glEnableVertexAttribArray(mHPosition);
        GLES20.glVertexAttribPointer(mHPosition,2, GLES20.GL_FLOAT, false, 0,mVerBuffer);
        GLES20.glEnableVertexAttribArray(mHCoord);
        GLES20.glVertexAttribPointer(mHCoord, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);
        GLES20.glDisableVertexAttribArray(mHPosition);
        GLES20.glDisableVertexAttribArray(mHCoord);
    }

这里的主要opengl指令相关的类在于CameraFilter:

/**
 * @CreadBy :DramaScript
 * @date 2017/7/10
 */
public class CameraFilter extends BaseFilter {

    //连续矩阵句柄
    private int mHCoordMatrix;
    //连续矩阵值
    private float[] mCoordMatrix= Arrays.copyOf(OM,16);

    public CameraFilter(Resources mRes) {
        super(mRes);
    }

    @Override
    protected void onCreate() {
        //创建opengl2
        createProgramByAssetsFile("shader/oes_base_vertex.sh","shader/oes_base_fragment.sh");
        mHCoordMatrix= GLES20.glGetUniformLocation(mProgram,"vCoordMatrix");
    }

    public void setCoordMatrix(float[] matrix){
        this.mCoordMatrix=matrix;
    }

    @Override
    protected void onBindTexture() {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0+getTextureType());
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,getTextureId());
        GLES20.glUniform1i(mHTexture,getTextureType());
    }

    @Override
    protected void onSetExpandData() {
        super.onSetExpandData();
        GLES20.glUniformMatrix4fv(mHCoordMatrix,1,false,mCoordMatrix,0);
    }

    @Override
    protected void onSizeChanged(int width, int height) {

    }
}

上面通过加载Assets目录下的sh文件来显示,sh写的是opengl相关的代码,其中的顶点着色器和片元着色器:

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 textureCoordinate;
uniform samplerExternalOES vTexture;
void main() {
    gl_FragColor = texture2D( vTexture, textureCoordinate );
}
attribute vec4 vPosition;
attribute vec2 vCoordinate;
uniform mat4 vMatrix;

varying vec2 aCoordinate;

void main(){
    gl_Position=vMatrix*vPosition;
    aCoordinate=vCoordinate;
}

其实主要还是整个流程,以及对opengl的一些相关术语的理解,如果不明opengl的相关术语描述。就这样,代码下载:
项目源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android 应用中使用 GLSurfaceViewCamera2 API 实现预览,可以参考以下步骤: 1. 在你的 Android 项目中添加 GLSurfaceView 控件,并在应用程序中初始化它。 2. 通过 Camera2 API 打开相机,并将相机输出连接到 GLSurfaceView 控件上。 3. 在 GLSurfaceView 控件中实现自定义的 Renderer,并在 Renderer 中实现图像渲染和处理逻辑。 4. 将渲染结果显示在 GLSurfaceView 控件上。 以下是一个简单的代码示例,演示如何使用 GLSurfaceViewCamera2 API 实现预览: ```java public class PreviewActivity extends AppCompatActivity { private CameraManager cameraManager; private CameraDevice cameraDevice; private CameraCaptureSession captureSession; private CaptureRequest.Builder previewRequestBuilder; private CaptureRequest previewRequest; private Size previewSize; private SurfaceTexture surfaceTexture; private GLSurfaceView glSurfaceView; private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraDevice = camera; createCameraPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { cameraDevice.close(); cameraDevice = null; } @Override public void onError(@NonNull CameraDevice camera, int error) { cameraDevice.close(); cameraDevice = null; } }; private void openCamera() { cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { String cameraId = cameraManager.getCameraIdList()[0]; CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); previewSize = map.getOutputSizes(SurfaceTexture.class)[0]; surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); Surface previewSurface = new Surface(surfaceTexture); previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); previewRequestBuilder.addTarget(previewSurface); cameraDevice.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; updatePreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } private void createCameraPreviewSession() { try { surfaceTexture = glSurfaceView.getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight()); openCamera(); } catch (CameraAccessException e) { e.printStackTrace(); } } private void updatePreview() { previewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); previewRequest = previewRequestBuilder.build(); try { captureSession.setRepeatingRequest(previewRequest, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); glSurfaceView = new GLSurfaceView(this); glSurfaceView.setEGLContextClientVersion(2); glSurfaceView.setRenderer(new PreviewRenderer()); setContentView(glSurfaceView); } @Override protected void onResume() { super.onResume(); if (glSurfaceView != null) { glSurfaceView.onResume(); } if (cameraDevice == null) { try { cameraManager.openCamera(cameraManager.getCameraIdList()[0], stateCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } } @Override protected void onPause() { if (glSurfaceView != null) { glSurfaceView.onPause(); } if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } super.onPause(); } private class PreviewRenderer implements GLSurfaceView.Renderer { private final float[] vertexData = { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f }; private final float[] textureData = { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f }; private int textureId; private int program; private int aPositionLocation; private int aTextureLocation; private int uTextureMatrixLocation; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { textureId = createTexture(); program = createProgram(); aPositionLocation = glGetAttribLocation(program, "aPosition"); aTextureLocation = glGetAttribLocation(program, "aTextureCoord"); uTextureMatrixLocation = glGetUniformLocation(program, "uTextureMatrix"); glClearColor(0f, 0f, 0f, 0f); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { glViewport(0, 0, width, height); Matrix.scaleM(textureMatrix, 0, 1f, -1f, 1f); Matrix.translateM(textureMatrix, 0, 0f, -1f, 0f); Matrix.rotateM(textureMatrix, 0, 90f, 0f, 0f, 1f); } @Override public void onDrawFrame(GL10 gl) { glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glEnableVertexAttribArray(aPositionLocation); glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 0, vertexBuffer); glEnableVertexAttribArray(aTextureLocation); glVertexAttribPointer(aTextureLocation, 2, GL_FLOAT, false, 0, textureBuffer); glUniformMatrix4fv(uTextureMatrixLocation, 1, false, textureMatrix, 0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(aPositionLocation); glDisableVertexAttribArray(aTextureLocation); } private int createTexture() { int[] textures = new int[1]; glGenTextures(1, textures, 0); int textureId = textures[0]; glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId); glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return textureId; } private int createProgram() { String vertexShaderCode = "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "uniform mat4 uTextureMatrix;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " vTextureCoord = (uTextureMatrix * aTextureCoord).xy;\n" + " gl_Position = aPosition;\n" + "}"; String fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES uTexture;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTextureCoord);\n" + "}"; int vertexShader = loadShader(GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentShaderCode); int program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); glLinkProgram(program); glUseProgram(program); return program; } private int loadShader(int type, String code) { int shader = glCreateShader(type); glShaderSource(shader, code); glCompileShader(shader); return shader; } } } ``` 需要注意的是,这只是一个简单的示例,并且可能需要进行进一步的优化和改进,以满足你的实际需求和性能要求。同时,为了确保应用程序的稳定性,还需要进行充分的测试和错误处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值