如何截取ArFragment的屏幕截图?

时间:2019-06-23 20:21:27

标签: android screenshot arcore

经过数小时的搜索,我终于可以保存ArFragment的屏幕截图。 但是问题在于它只保存了摄像机的当前图像,除了已放置的3D对象之外。

我如何获取完整的屏幕截图(摄像机的当前图像+放置的3D对象)?

我在下面使用的代码。

ImageButton btn3 = (ImageButton)findViewById(R.id.camera_btn);
btn3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        onSceneUpdate((FrameTime) frameTime);

        Toast.makeText(AR_Activity.this, "스크린샷이 저장되었습니다.", Toast.LENGTH_SHORT).show();
    }
});

private void onSceneUpdate(FrameTime frameTime) {
    try {
        Date now = new Date();
        android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
        String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
        Frame currentFrame = arFragment.getArSceneView().getArFrame();
        Image currentImage = currentFrame.acquireCameraImage();
        int imageFormat = currentImage.getFormat();
        if (imageFormat == ImageFormat.YUV_420_888) {
            Log.d("ImageFormat", "Image format is YUV_420_888");
        }

        WriteImageInformation((Image) currentImage, (String) mPath);
    } catch (Exception e) {

    }
}

private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
    yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
    return out.toByteArray();
}

public static void WriteImageInformation(Image image, String path) {

    byte[] data = null;
    data = NV21toJPEG(YUV_420_888toNV21(image),
            image.getWidth(), image.getHeight());
    BufferedOutputStream bos = null;
    try {
        bos = new BufferedOutputStream(new FileOutputStream(path));
        bos.write(data);
        bos.flush();
        bos.close();

    } catch (Throwable e) {
        e.printStackTrace();
    }
}

private static byte[] YUV_420_888toNV21(Image image) {
    byte[] nv21;
    ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
    ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
    ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    nv21 = new byte[ySize + uSize + vSize];

    //U and V are swapped
    yBuffer.get(nv21, 0, ySize);
    vBuffer.get(nv21, ySize, vSize);
    uBuffer.get(nv21, ySize + vSize, uSize);

    return nv21;
}

1 个答案:

答案 0 :(得分:0)

使用PixelCopy。效果很好。

对于那些可能会想知道的人, 我将在下面添加我的代码。

ImageButton btn3 = (ImageButton)findViewById(R.id.camera_btn);
    btn3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            takePhoto();
        }
    }); 
private String generateFilename() {

    //현재시간을 기준으로 파일 이름 생성
    String date =
            new SimpleDateFormat("yyyyMMddHHmmss", java.util.Locale.getDefault()).format(new Date());
    return Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES) + File.separator + "IM/" + date + "_screenshot.jpg";
}

private void saveBitmapToDisk(Bitmap bitmap, String filename) throws IOException {

    //사용자의 갤러리에 IM 디렉토리 생성 및 Bitmap 을 JPEG 형식으로 갤러리에 저장
    File out = new File(filename);
    if (!out.getParentFile().exists()) {
        out.getParentFile().mkdirs();
    }
    try (FileOutputStream outputStream = new FileOutputStream(filename);
         ByteArrayOutputStream outputData = new ByteArrayOutputStream()) {
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputData);
        outputData.writeTo(outputStream);
        outputStream.flush();
        outputStream.close();
    } catch (IOException ex) {
        throw new IOException("Failed to save bitmap to disk", ex);
    }
}

private void takePhoto(){
    //PixelCopy 를 사용하여 카메라 화면과 object 를 bitmap 으로 생성
    final String filename = generateFilename();
    ArSceneView view = arFragment.getArSceneView();

    final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(),
            Bitmap.Config.ARGB_8888);

    final HandlerThread handlerThread = new HandlerThread("PixelCopier");
    handlerThread.start();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        PixelCopy.request(view, bitmap, (copyResult) -> {
            if (copyResult == PixelCopy.SUCCESS) {
                try {
                    saveBitmapToDisk(bitmap, filename);

                    //Media Scanning 실시
                    Uri uri = Uri.parse("file://" + filename);
                    Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    i.setData(uri);
                    sendBroadcast(i);

                } catch (IOException e) {
                    Toast toast = Toast.makeText(AR_Activity.this, e.toString(),
                            Toast.LENGTH_LONG);
                    toast.show();
                    return;
                }
                Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
                        "스크린샷이 저장되었습니다.", Snackbar.LENGTH_LONG);
                snackbar.setAction("갤러리에서 보기", v -> {
                    //어플 내에서 저장한 스크린샷을 확인 가능
                    File photoFile = new File(filename);

                    Uri photoURI = FileProvider.getUriForFile(AR_Activity.this,
                            AR_Activity.this.getPackageName() + ".ar.codelab.name.provider",
                            photoFile);
                    Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
                    intent.setDataAndType(photoURI, "image/*");
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    startActivity(intent);
                });
                snackbar.show();
            } else {
                Toast toast = Toast.makeText(AR_Activity.this,
                        "스크린샷 저장 실패!: " + copyResult, Toast.LENGTH_LONG);
                toast.show();
            }
            handlerThread.quitSafely();
        }, new Handler(handlerThread.getLooper()));
    }
}