Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfd2380f authored by Dichen Zhang's avatar Dichen Zhang
Browse files

Add EXIF support to JPEG/R Java encoding API

Bug: b/299202809
Test: YuvImageTest.java
Change-Id: If1aa9598f75062e7d0684d5d0f4b60f1e4a19f4d
parent ad930513
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -16932,6 +16932,7 @@ package android.graphics {
    ctor public YuvImage(@NonNull byte[], int, int, int, @Nullable int[], @NonNull android.graphics.ColorSpace);
    method public boolean compressToJpeg(android.graphics.Rect, int, java.io.OutputStream);
    method public boolean compressToJpegR(@NonNull android.graphics.YuvImage, int, @NonNull java.io.OutputStream);
    method @FlaggedApi("com.android.graphics.flags.yuv_image_compress_to_ultra_hdr") public boolean compressToJpegR(@NonNull android.graphics.YuvImage, int, @NonNull java.io.OutputStream, @NonNull byte[]);
    method @NonNull public android.graphics.ColorSpace getColorSpace();
    method public int getHeight();
    method public int[] getStrides();
+7 −0
Original line number Diff line number Diff line
@@ -6,3 +6,10 @@ flag {
     description: "Add a function without unused exact param for computeBounds."
     bug: "304478551"
}

flag {
     name: "yuv_image_compress_to_ultra_hdr"
     namespace: "core_graphics"
     description: "Feature flag for YUV image compress to Ultra HDR."
     bug: "308978825"
}
 No newline at end of file
+38 −3
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.graphics;

import com.android.graphics.flags.Flags;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.io.OutputStream;
@@ -269,6 +272,38 @@ public class YuvImage {
   */
    public boolean compressToJpegR(@NonNull YuvImage sdr, int quality,
            @NonNull OutputStream stream) {
        byte[] emptyExif = new byte[0];
        return compressToJpegR(sdr, quality, stream, emptyExif);
    }

    /**
     * Compress the HDR image into JPEG/R format.
     *
     * Sample usage:
     *     hdr_image.compressToJpegR(sdr_image, 90, stream);
     *
     * For the SDR image, only YUV_420_888 image format is supported, and the following
     * color spaces are supported:
     *     ColorSpace.Named.SRGB,
     *     ColorSpace.Named.DISPLAY_P3
     *
     * For the HDR image, only YCBCR_P010 image format is supported, and the following
     * color spaces are supported:
     *     ColorSpace.Named.BT2020_HLG,
     *     ColorSpace.Named.BT2020_PQ
     *
     * @param sdr       The SDR image, only ImageFormat.YUV_420_888 is supported.
     * @param quality   Hint to the compressor, 0-100. 0 meaning compress for
     *                  small size, 100 meaning compress for max quality.
     * @param stream    OutputStream to write the compressed data.
     * @param exif      Exchangeable image file format.
     * @return          True if the compression is successful.
     * @throws IllegalArgumentException if input images are invalid; quality is not within [0,
     *                  100]; or stream is null.
     */
    @FlaggedApi(Flags.FLAG_YUV_IMAGE_COMPRESS_TO_ULTRA_HDR)
    public boolean compressToJpegR(@NonNull YuvImage sdr, int quality,
            @NonNull OutputStream stream, @NonNull byte[] exif) {
        if (sdr == null) {
            throw new IllegalArgumentException("SDR input cannot be null");
        }
@@ -304,7 +339,7 @@ public class YuvImage {
      return nativeCompressToJpegR(mData, mColorSpace.getDataSpace(),
                                   sdr.getYuvData(), sdr.getColorSpace().getDataSpace(),
                                   mWidth, mHeight, quality, stream,
                                   new byte[WORKING_COMPRESS_STORAGE]);
                                   new byte[WORKING_COMPRESS_STORAGE], exif);
  }


@@ -416,5 +451,5 @@ public class YuvImage {

    private static native boolean nativeCompressToJpegR(byte[] hdr, int hdrColorSpaceId,
            byte[] sdr, int sdrColorSpaceId, int width, int height, int quality,
            OutputStream stream, byte[] tempStorage);
            OutputStream stream, byte[] tempStorage, byte[] exif);
}
+12 −5
Original line number Diff line number Diff line
@@ -332,7 +332,7 @@ ultrahdr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNI

bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
        SkWStream* stream, void* hdr, int hdrColorSpace, void* sdr, int sdrColorSpace,
        int width, int height, int jpegQuality) {
        int width, int height, int jpegQuality, ScopedByteArrayRO* jExif) {
    // Check SDR color space. Now we only support SRGB transfer function
    if ((sdrColorSpace & ADataSpace::TRANSFER_MASK) !=  ADataSpace::TRANSFER_SRGB) {
        jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
@@ -365,6 +365,10 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
    yuv420.height = height;
    yuv420.colorGamut = sdrColorGamut;

    jpegr_exif_struct exif;
    exif.data = const_cast<void*>(reinterpret_cast<const void*>(jExif->get()));
    exif.length = jExif->size();

    jpegr_compressed_struct jpegR;
    jpegR.maxLength = width * height * sizeof(uint8_t);

@@ -373,7 +377,8 @@ bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,

    if (int success = jpegREncoder.encodeJPEGR(&p010, &yuv420,
            hdrTransferFunction,
            &jpegR, jpegQuality, nullptr); success != android::OK) {
            &jpegR, jpegQuality,
            exif.length > 0 ? &exif : NULL); success != android::OK) {
        ALOGW("Encode JPEG/R failed, error code: %d.", success);
        return false;
    }
@@ -415,15 +420,17 @@ static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
static jboolean YuvImage_compressToJpegR(JNIEnv* env, jobject, jbyteArray inHdr,
        jint hdrColorSpace, jbyteArray inSdr, jint sdrColorSpace,
        jint width, jint height, jint quality, jobject jstream,
        jbyteArray jstorage) {
        jbyteArray jstorage, jbyteArray jExif) {
    jbyte* hdr = env->GetByteArrayElements(inHdr, NULL);
    jbyte* sdr = env->GetByteArrayElements(inSdr, NULL);
    ScopedByteArrayRO exif(env, jExif);

    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
    P010Yuv420ToJpegREncoder encoder;

    jboolean result = JNI_FALSE;
    if (encoder.encode(env, strm, hdr, hdrColorSpace, sdr, sdrColorSpace,
                       width, height, quality)) {
                       width, height, quality, &exif)) {
        result = JNI_TRUE;
    }

@@ -437,7 +444,7 @@ static jboolean YuvImage_compressToJpegR(JNIEnv* env, jobject, jbyteArray inHdr,
static const JNINativeMethod gYuvImageMethods[] = {
    {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z",
        (void*)YuvImage_compressToJpeg },
    {   "nativeCompressToJpegR",  "([BI[BIIIILjava/io/OutputStream;[B)Z",
    {   "nativeCompressToJpegR",  "([BI[BIIIILjava/io/OutputStream;[B[B)Z",
        (void*)YuvImage_compressToJpegR }
};

+3 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_

#include <android/data_space.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <ultrahdr/jpegr.h>

extern "C" {
@@ -90,11 +91,12 @@ public:
     *  @param width Width of the Yuv data in terms of pixels.
     *  @param height Height of the Yuv data in terms of pixels.
     *  @param jpegQuality Picture quality in [0, 100].
     *  @param exif Buffer holds EXIF package.
     *  @return true if successfully compressed the stream.
     */
    bool encode(JNIEnv* env,
            SkWStream* stream, void* hdr, int hdrColorSpace, void* sdr, int sdrColorSpace,
            int width, int height, int jpegQuality);
            int width, int height, int jpegQuality, ScopedByteArrayRO* exif);

    /** Map data space (defined in DataSpace.java and data_space.h) to the color gamut
     *  used in JPEG/R