Image2Bitmap.java

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.core.imagecapture;

import static androidx.camera.core.ImageCapture.ERROR_UNKNOWN;
import static androidx.camera.core.ImageProcessingUtil.convertYUVToRGB;

import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.Build;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.ImageReaderProxys;
import androidx.camera.core.SafeCloseImageReaderProxy;
import androidx.camera.core.internal.utils.ImageUtil;
import androidx.camera.core.processing.Operation;
import androidx.camera.core.processing.Packet;

import java.nio.ByteBuffer;

/**
 * Convert an {@link ImageProxy} to a {@link Bitmap}.
 *
 * <p>An {@link ImageCaptureException} will be thrown if the conversion failed.
 * Currently it supports only {@link ImageFormat#YUV_420_888} and
 * {@link ImageFormat#JPEG} image. {@link IllegalArgumentException} will be thrown if the input
 * image format is not supported.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class Image2Bitmap implements
        Operation<Packet<ImageProxy>, Bitmap> {
    @NonNull
    @Override
    public Bitmap apply(@NonNull Packet<ImageProxy> imageProxyPacket)
            throws ImageCaptureException {
        Bitmap result;
        SafeCloseImageReaderProxy rgbImageReader = null;
        try {
            if (imageProxyPacket.getFormat() == ImageFormat.YUV_420_888) {
                ImageProxy yuvImage = imageProxyPacket.getData();
                boolean needFlip = (imageProxyPacket.getRotationDegrees() % 180) != 0;
                int tempImageReaderWidth = needFlip ? yuvImage.getHeight() : yuvImage.getWidth();
                int tempImageReaderHeight = needFlip ? yuvImage.getWidth() : yuvImage.getHeight();

                // TODO(b/313548792): remove the usage of ImageReader by creating a version of
                //  convertYUVToBitmap that also rotates the output.
                rgbImageReader = new SafeCloseImageReaderProxy(
                        ImageReaderProxys.createIsolatedReader(
                                tempImageReaderWidth, tempImageReaderHeight,
                                PixelFormat.RGBA_8888, 2)
                );

                ByteBuffer rgbConvertedBuffer = ByteBuffer.allocateDirect(
                        yuvImage.getWidth() * yuvImage.getHeight() * 4);
                ImageProxy imageProxyRGB = convertYUVToRGB(
                        yuvImage,
                        rgbImageReader,
                        rgbConvertedBuffer,
                        imageProxyPacket.getRotationDegrees(),
                        /* onePixelShiftEnabled */false);
                yuvImage.close();
                if (imageProxyRGB == null) {
                    throw new ImageCaptureException(ERROR_UNKNOWN, "Can't covert YUV to RGB", null);
                }
                Bitmap bitmap = ImageUtil.createBitmapFromImageProxy(imageProxyRGB);
                imageProxyRGB.close();
                result = bitmap;
            } else if (imageProxyPacket.getFormat() == ImageFormat.JPEG) {
                ImageProxy jpegImage = imageProxyPacket.getData();
                Bitmap bitmap = ImageUtil.createBitmapFromImageProxy(jpegImage);
                jpegImage.close();
                result = ImageUtil.rotateBitmap(bitmap, imageProxyPacket.getRotationDegrees());
            } else {
                throw new IllegalArgumentException("Invalid postview image format : "
                        + imageProxyPacket.getFormat());
            }
            return result;
        } catch (UnsupportedOperationException e) {
            String format = imageProxyPacket.getFormat() == ImageFormat.YUV_420_888
                    ? "YUV" : "JPEG";
            throw new ImageCaptureException(ERROR_UNKNOWN,
                    "Can't convert " + format + " to bitmap", e);
        } finally {
            if (rgbImageReader != null) {
                rgbImageReader.close();
            }
        }
    }
}