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

Commit b8df8e07 authored by Ruben Brunk's avatar Ruben Brunk
Browse files

DNG: Write out additional fields.

Bug: 15112503

Change-Id: Ib06d9a5e70e6e3d5063a95a7109538ef64f03334
parent dc925857
Loading
Loading
Loading
Loading
+33 −2
Original line number Diff line number Diff line
@@ -22,12 +22,16 @@ import android.hardware.camera2.impl.CameraMetadataNative;
import android.location.Location;
import android.media.ExifInterface;
import android.media.Image;
import android.os.SystemClock;
import android.util.Size;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;

/**
 * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
@@ -55,6 +59,7 @@ import java.nio.ByteBuffer;
 */
public final class DngCreator implements AutoCloseable {

    private static final String TAG = "DngCreator";
    /**
     * Create a new DNG object.
     *
@@ -75,7 +80,25 @@ public final class DngCreator implements AutoCloseable {
        if (characteristics == null || metadata == null) {
            throw new NullPointerException("Null argument to DngCreator constructor");
        }
        nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());

        // Find current time
        long currentTime = System.currentTimeMillis();

        // Find boot time
        long bootTimeMillis = currentTime - SystemClock.elapsedRealtime();

        // Find capture time (nanos since boot)
        Long timestamp = metadata.get(CaptureResult.SENSOR_TIMESTAMP);
        long captureTime = currentTime;
        if (timestamp != null) {
            captureTime = timestamp / 1000000 + bootTimeMillis;
        }

        // Format for metadata
        String formattedCaptureTime = sDateTimeStampFormat.format(captureTime);

        nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy(),
                formattedCaptureTime);
    }

    /**
@@ -329,6 +352,13 @@ public final class DngCreator implements AutoCloseable {
        }
    }

    private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd kk:mm:ss";
    private static final DateFormat sDateTimeStampFormat =
            new SimpleDateFormat(TIFF_DATETIME_FORMAT);

    static {
        sDateTimeStampFormat.setTimeZone(TimeZone.getDefault());
    }
    /**
     * This field is used by native code, do not access or modify.
     */
@@ -337,7 +367,8 @@ public final class DngCreator implements AutoCloseable {
    private static native void nativeClassInit();

    private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
                                                CameraMetadataNative nativeResult);
                                                CameraMetadataNative nativeResult,
                                                String captureTime);

    private synchronized native void nativeDestroy();

+175 −3
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@
#include <utils/RefBase.h>
#include <cutils/properties.h>

#include <string.h>

#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_hardware_camera2_CameraMetadata.h"

@@ -175,7 +177,7 @@ static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
}

static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
        jobject resultsPtr) {
        jobject resultsPtr, jstring formattedCaptureTime) {
    ALOGV("%s:", __FUNCTION__);
    CameraMetadata characteristics;
    CameraMetadata results;
@@ -205,7 +207,6 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;

    // TODO: Greensplit.
    // TODO: UniqueCameraModel
    // TODO: Add remaining non-essential tags
    {
        // Set orientation
@@ -351,6 +352,176 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
                env, TAG_CFALAYOUT);
    }

    {
        // image description
        uint8_t imageDescription = '\0'; // empty
        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
                env, TAG_IMAGEDESCRIPTION);
    }

    {
        // make
        char manufacturer[PROPERTY_VALUE_MAX];

        // Use "" to represent unknown make as suggested in TIFF/EP spec.
        property_get("ro.product.manufacturer", manufacturer, "");
        uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;

        BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
                TIFF_IFD_0), env, TAG_MAKE);
    }

    {
        // model
        char model[PROPERTY_VALUE_MAX];

        // Use "" to represent unknown model as suggested in TIFF/EP spec.
        property_get("ro.product.model", model, "");
        uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;

        BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
                TIFF_IFD_0), env, TAG_MODEL);
    }

    {
        // x resolution
        uint32_t xres[] = { 72, 1 }; // default 72 ppi
        BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
                env, TAG_XRESOLUTION);

        // y resolution
        uint32_t yres[] = { 72, 1 }; // default 72 ppi
        BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
                env, TAG_YRESOLUTION);

        uint16_t unit = 2; // inches
        BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
                env, TAG_RESOLUTIONUNIT);
    }

    {
        // software
        char software[PROPERTY_VALUE_MAX];
        property_get("ro.build.fingerprint", software, "");
        uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
        BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
                TIFF_IFD_0), env, TAG_SOFTWARE);
    }

    {
        // datetime
        const size_t DATETIME_COUNT = 20;
        const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);

        size_t len = strlen(captureTime) + 1;
        if (len != DATETIME_COUNT) {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "Timestamp string length is not required 20 characters");
            return;
        }

        BAIL_IF_INVALID(writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
                reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);

        // datetime original
        BAIL_IF_INVALID(writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
                reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
        env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
    }

    {
        // TIFF/EP standard id
        uint8_t standardId[] = { 1, 0, 0, 0 };
        BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
                TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID);
    }

    {
        // copyright
        uint8_t copyright = '\0'; // empty
        BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
                TIFF_IFD_0), env, TAG_COPYRIGHT);
    }

    {
        // exposure time
        camera_metadata_entry entry =
            results.find(ANDROID_SENSOR_EXPOSURE_TIME);
        BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME);

        int64_t exposureTime = *(entry.data.i64);

        if (exposureTime < 0) {
            // Should be unreachable
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "Negative exposure time in metadata");
            return;
        }

        // Ensure exposure time doesn't overflow (for exposures > 4s)
        uint32_t denominator = 1000000000;
        while (exposureTime > UINT32_MAX) {
            exposureTime >>= 1;
            denominator >>= 1;
            if (denominator == 0) {
                // Should be unreachable
                jniThrowException(env, "java/lang/IllegalArgumentException",
                        "Exposure time too long");
                return;
            }
        }

        uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
        BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
                TIFF_IFD_0), env, TAG_EXPOSURETIME);

    }

    {
        // ISO speed ratings
        camera_metadata_entry entry =
            results.find(ANDROID_SENSOR_SENSITIVITY);
        BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS);

        int32_t tempIso = *(entry.data.i32);
        if (tempIso < 0) {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                                    "Negative ISO value");
            return;
        }

        if (tempIso > UINT16_MAX) {
            ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
            tempIso = UINT16_MAX;
        }

        uint16_t iso = static_cast<uint16_t>(tempIso);
        BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
                TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS);
    }

    {
        // focal length
        camera_metadata_entry entry =
            results.find(ANDROID_LENS_FOCAL_LENGTH);
        BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH);

        uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
        BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
                TIFF_IFD_0), env, TAG_FOCALLENGTH);
    }

    {
        // f number
        camera_metadata_entry entry =
            results.find(ANDROID_LENS_APERTURE);
        BAIL_IF_EMPTY(entry, env, TAG_FNUMBER);

        uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
        BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
                TIFF_IFD_0), env, TAG_FNUMBER);
    }

    {
        // Set DNG version information
        uint8_t version[4] = {1, 4, 0, 0};
@@ -751,7 +922,8 @@ static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject
static JNINativeMethod gDngCreatorMethods[] = {
    {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
    {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
            "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
            "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
            (void*) DngCreator_init},
    {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
    {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
    {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",