0


OpenGLES:glReadPixels()获取相机GLSurfaceView预览数据并保存

Android现行的Camera API2机制可以通过onImageAvailable(ImageReader reader)回调从底层获取到Jpeg、YuvRaw三种格式的Image,然后通过保存Image实现拍照功能,但是却并没有Api能直接在上层直接拿到实时预览的数据。

Android Camera预览的实现是上层下发SurfaceCameraHAL,由CameraHAL也就是android.hardware.camera.provider@2.4-service进程往Surface对应的Buffer中填充预览数据,然后再copySurfaceFling中由OpenGL进行渲染显示。

实际相机开发中,不仅仅只是要实现预览,还经常需要拿到预览数据做一些特效处理,那么问题来了,怎么在相机App获取到实时预览数据呢?

这跟上层Camera App用于显示SurfaceView控件有关:

  • 如果上层使用的是GLSurfaceView,可以直接通过OpenGLESglReadPixels()获取到copy到显存中的预览数据
  • 如果上层使用的不是GLSurfaceView,可以通过自己搭建EGL环境,然后在EGL环境中调用OpenGLES的**glReadPixels()**获取到预览数据。

GLSurfaceView其实就是Android封装好的EGL+SufaceView控件,Android的所有渲染最终都是通过OpenGL来实现的,所以万变不离其宗,本质上上层Camera App都只能通过OpenGLES的**glReadPixels()**实现预览数据的获取。

一个SurfaceAndroid EGL中对应一个FrameBuffer,学习过OpenGL的应该都知道,一个FrameBuffer会有多个附着**(attachment),其中必须且只能有一个ColorBuffer附着,有一个或多个StencilBufferDepthBuffer附着。**

glReadPixels()仅限于读取ColorBuffer,无法读取DepthBufferStencilBuffer,它可以将图像内容从显存读取到内存中,将ColorBuffer中的像素值保存到预分配的内存缓冲区。

前面关于OpenGLES的博文中,有两篇是使用OpenGLES实现相机的相关功能,一篇是《OpenGLES:GLSurfaceView实现Android Camera预览》,一篇是《OpenGLES:相机实时滤镜四宫格、九宫格》,今天就在这两篇博文基础上实现相机预览数据的获取和保存。

相机实现部分在此不做过多讲解,有兴趣的可以参看前面两篇博文,有详细的讲解
本文主要展示**glReadPixels()**对相机预览数据获取的实现

代码实现其实很简单
GLSurfaceView.Renderer实现类的**onDrawFrame(GL10 gl)**函数中新增如下代码段:

  1. if (shouldTakePic) {
  2. //预览尺寸
  3. int w = 1080;int h = 1440;
  4. //预览数据保存成照片的目录
  5. String savePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/MyCamera/";
  6. int[] iat = new int[w * h];
  7. IntBuffer ib = IntBuffer.allocate(w * h);
  8. //(0,580)距离屏幕左下角的距离,与glViewport(0, 580,...)保持一致
  9. glReadPixels(0, 580, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ib);
  10. int[] ia = ib.array();
  11. //glReadPixels 读取的内容是上下翻转的,要处理一下
  12. for (int i = 0; i < h; i++) {
  13. for (int j = 0; j < w; j++) {
  14. iat[(h - i - 1) * w + j] = ia[i * w + j];
  15. }
  16. }
  17. Bitmap inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  18. inBitmap.copyPixelsFromBuffer(IntBuffer.wrap(iat));
  19. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  20. inBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);
  21. byte[] bitmapData = bos.toByteArray();
  22. File tempDir = new File(savePath);
  23. tempDir.mkdirs();
  24. String fileName = "temp_" + System.currentTimeMillis() + ".jpeg";
  25. File imgFile = new File(savePath, fileName);
  26. try {
  27. FileOutputStream output = new FileOutputStream(imgFile);
  28. output.write(bitmapData);
  29. output.flush();
  30. output.close();
  31. Log.v(TAG, "ImageReader X");
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. } finally {
  35. inBitmap.recycle();
  36. }
  37. }

glReadPixels读取的内容上下翻转处理还有另外一种实现,
原理都是一样的,实现起来大同小异

  1. if (shouldTakePic) {
  2. //预览尺寸
  3. int w = 1080;
  4. int h = 1440;
  5. //预览数据保存成照片的目录
  6. String savePath = Environment.getExternalStorageDirectory().getPath() + "/DCIM/MyCamera/";
  7. int b[] = new int[(int) (w * h)];
  8. int bt[] = new int[(int) (w * h)];
  9. IntBuffer buffer = IntBuffer.wrap(b);
  10. buffer.position(0);
  11. //(0,580)距离屏幕左下角的距离,与glViewport(0, 580,...)保持一致
  12. glReadPixels(0, 580, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  13. for (int i = 0; i < h; i++) {
  14. for (int j = 0; j < w; j++) {
  15. int pix = b[i * w + j];
  16. int pb = (pix >> 16) & 0xff;
  17. int pr = (pix << 16) & 0x00ff0000;
  18. int pix1 = (pix & 0xff00ff00) | pr | pb;
  19. bt[(h - i - 1) * w + j] = pix1;
  20. }
  21. }
  22. Bitmap inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  23. inBitmap.copyPixelsFromBuffer(buffer);
  24. inBitmap = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
  25. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  26. inBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);
  27. byte[] bitmapData = bos.toByteArray();
  28. ByteArrayInputStream fis = new ByteArrayInputStream(bitmapData);
  29. String tempPicFile = "temp_" + System.currentTimeMillis() + ".jpeg";
  30. File tempDir = new File(savePath);
  31. tempDir.mkdirs();
  32. try {
  33. File tmpFile = new File(tempDir, tempPicFile);
  34. FileOutputStream fos = new FileOutputStream(tmpFile);
  35. byte[] buf = new byte[1024];
  36. int len;
  37. while ((len = fis.read(buf)) > 0) {
  38. fos.write(buf, 0, len);
  39. }
  40. fis.close();
  41. fos.close();
  42. inBitmap.recycle();
  43. } catch (FileNotFoundException e) {
  44. e.printStackTrace();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. }

验证下效果,抓两张预览照试试:

抓一张普通预览:

抓一张四宫格滤镜预览:


本文转载自: https://blog.csdn.net/geyichongchujianghu/article/details/134912392
版权归原作者 肖云鹤 所有, 如有侵权,请联系我们删除。

“OpenGLES:glReadPixels()获取相机GLSurfaceView预览数据并保存”的评论:

还没有评论