summaryrefslogtreecommitdiffstats
path: root/core/jni
diff options
context:
space:
mode:
authorRuben Brunk <rubenbrunk@google.com>2014-05-22 12:33:54 -0700
committerRuben Brunk <rubenbrunk@google.com>2014-05-22 22:57:11 +0000
commitb6079005ed0631c3972ff427f56e12523ec214a7 (patch)
tree0ed9977a4c689a07702302c72dc1622ce7ae835f /core/jni
parent88aca033ff10d3298f06b4c950ef24ec091185b7 (diff)
downloadframeworks_base-b6079005ed0631c3972ff427f56e12523ec214a7.zip
frameworks_base-b6079005ed0631c3972ff427f56e12523ec214a7.tar.gz
frameworks_base-b6079005ed0631c3972ff427f56e12523ec214a7.tar.bz2
camera2: Move DngCreator to hardware/camera2.
Bug: 15091253 Change-Id: Ibe2ee376436e5c796e33c0b782ea68278589f2e6
Diffstat (limited to 'core/jni')
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_hardware_camera2_DngCreator.cpp774
3 files changed, 777 insertions, 0 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 835a648..a159715 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -138,6 +138,7 @@ LOCAL_SRC_FILES:= \
android_hardware_Camera.cpp \
android_hardware_camera2_CameraMetadata.cpp \
android_hardware_camera2_legacy_LegacyCameraDevice.cpp \
+ android_hardware_camera2_DngCreator.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
android_hardware_UsbDevice.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2d350e0..0c7eefa 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -80,6 +80,7 @@ extern int register_android_opengl_jni_GLES31Ext(JNIEnv* env);
extern int register_android_hardware_Camera(JNIEnv *env);
extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env);
extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *env);
+extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
extern int register_android_hardware_UsbDevice(JNIEnv *env);
@@ -1286,6 +1287,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
+ REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
REG_JNI(register_android_hardware_UsbDevice),
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
new file mode 100644
index 0000000..7b686e7
--- /dev/null
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -0,0 +1,774 @@
+/*
+ * Copyright 2014 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DngCreator_JNI"
+
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+#include <img_utils/DngUtils.h>
+#include <img_utils/TagDefinitions.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
+#include <img_utils/Output.h>
+
+#include <utils/Log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+#include <cutils/properties.h>
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+using namespace android;
+using namespace img_utils;
+
+#define BAIL_IF_INVALID(expr, jnienv, tagId) \
+ if ((expr) != OK) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Invalid metadata for tag %x", tagId); \
+ return; \
+ }
+
+#define BAIL_IF_EMPTY(entry, jnienv, tagId) \
+ if (entry.count == 0) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Missing metadata fields for tag %x", tagId); \
+ return; \
+ }
+
+#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
+
+static struct {
+ jfieldID mNativeContext;
+} gDngCreatorClassInfo;
+
+static struct {
+ jmethodID mWriteMethod;
+} gOutputStreamClassInfo;
+
+enum {
+ BITS_PER_SAMPLE = 16,
+ BYTES_PER_SAMPLE = 2,
+ TIFF_IFD_0 = 0
+};
+
+// ----------------------------------------------------------------------------
+
+// This class is not intended to be used across JNI calls.
+class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
+public:
+ JniOutputStream(JNIEnv* env, jobject outStream);
+
+ virtual ~JniOutputStream();
+
+ status_t open();
+ status_t write(const uint8_t* buf, size_t offset, size_t count);
+ status_t close();
+private:
+ enum {
+ BYTE_ARRAY_LENGTH = 1024
+ };
+ jobject mOutputStream;
+ JNIEnv* mEnv;
+ jbyteArray mByteArray;
+};
+
+JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
+ mEnv(env) {
+ mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
+ if (mByteArray == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
+ }
+}
+
+JniOutputStream::~JniOutputStream() {
+ mEnv->DeleteLocalRef(mByteArray);
+}
+
+status_t JniOutputStream::open() {
+ // Do nothing
+ return OK;
+}
+
+status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
+ while(count > 0) {
+ size_t len = BYTE_ARRAY_LENGTH;
+ len = (count > len) ? len : count;
+ mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
+ 0, len);
+
+ if (mEnv->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+
+ count -= len;
+ offset += len;
+ }
+ return OK;
+}
+
+status_t JniOutputStream::close() {
+ // Do nothing
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
+
+extern "C" {
+
+static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ return reinterpret_cast<TiffWriter*>(env->GetLongField(thiz,
+ gDngCreatorClassInfo.mNativeContext));
+}
+
+static void DngCreator_setCreator(JNIEnv* env, jobject thiz, sp<TiffWriter> writer) {
+ ALOGV("%s:", __FUNCTION__);
+ TiffWriter* current = DngCreator_getCreator(env, thiz);
+ if (writer != NULL) {
+ writer->incStrong((void*) DngCreator_setCreator);
+ }
+ if (current) {
+ current->decStrong((void*) DngCreator_setCreator);
+ }
+ env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
+ reinterpret_cast<jlong>(writer.get()));
+}
+
+static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
+ ALOGV("%s:", __FUNCTION__);
+
+ gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
+ ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
+ "can't find android/hardware/camera2/DngCreator.%s",
+ ANDROID_DNGCREATOR_CTX_JNI_ID);
+
+ jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
+ LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
+ gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
+ LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
+}
+
+static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
+ jobject resultsPtr) {
+ ALOGV("%s:", __FUNCTION__);
+ CameraMetadata characteristics;
+ CameraMetadata results;
+ if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for camera characteristics.");
+ return;
+ }
+ if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "No native metadata defined for capture results.");
+ return;
+ }
+
+ sp<TiffWriter> writer = new TiffWriter();
+
+ writer->addIfd(TIFF_IFD_0);
+
+ status_t err = OK;
+
+ const uint32_t samplesPerPixel = 1;
+ const uint32_t bitsPerSample = BITS_PER_SAMPLE;
+ const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
+ uint32_t imageWidth = 0;
+ uint32_t imageHeight = 0;
+
+ OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+
+ // TODO: Greensplit.
+ // TODO: UniqueCameraModel
+ // TODO: Add remaining non-essential tags
+ {
+ // Set orientation
+ uint16_t orientation = 1; // Normal
+ BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
+ TAG_ORIENTATION);
+ }
+
+ {
+ // Set subfiletype
+ uint32_t subfileType = 0; // Main image
+ BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
+ TAG_NEWSUBFILETYPE);
+ }
+
+ {
+ // Set bits per sample
+ uint16_t bits = static_cast<uint16_t>(bitsPerSample);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
+ TAG_BITSPERSAMPLE);
+ }
+
+ {
+ // Set compression
+ uint16_t compression = 1; // None
+ BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
+ TAG_COMPRESSION);
+ }
+
+ {
+ // Set dimensions
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH);
+ uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
+ uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
+ TAG_IMAGEWIDTH);
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
+ TAG_IMAGELENGTH);
+ imageWidth = width;
+ imageHeight = height;
+ }
+
+ {
+ // Set photometric interpretation
+ uint16_t interpretation = 32803;
+ BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
+ TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION);
+ }
+
+ {
+ // Set blacklevel tags
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL);
+ const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
+ TAG_BLACKLEVEL);
+
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
+ TAG_BLACKLEVELREPEATDIM);
+ }
+
+ {
+ // Set samples per pixel
+ uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
+ BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
+ env, TAG_SAMPLESPERPIXEL);
+ }
+
+ {
+ // Set planar configuration
+ uint16_t config = 1; // Chunky
+ BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
+ env, TAG_PLANARCONFIGURATION);
+ }
+
+ {
+ // Set CFA pattern dimensions
+ uint16_t repeatDim[2] = {2, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
+ env, TAG_CFAREPEATPATTERNDIM);
+ }
+
+ {
+ // Set CFA pattern
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN);
+ camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
+ static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
+ entry.data.u8[0]);
+ switch(cfa) {
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
+ uint8_t cfa[4] = {0, 1, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
+ uint8_t cfa[4] = {1, 0, 2, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
+ uint8_t cfa[4] = {1, 2, 0, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
+ break;
+ }
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
+ uint8_t cfa[4] = {2, 1, 1, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
+ env, TAG_CFAPATTERN);
+ opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
+ break;
+ }
+ default: {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid metadata for tag %d", TAG_CFAPATTERN);
+ return;
+ }
+ }
+ }
+
+ {
+ // Set CFA plane color
+ uint8_t cfaPlaneColor[3] = {0, 1, 2};
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
+ env, TAG_CFAPLANECOLOR);
+ }
+
+ {
+ // Set CFA layout
+ uint16_t cfaLayout = 1;
+ BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
+ env, TAG_CFALAYOUT);
+ }
+
+ {
+ // Set DNG version information
+ uint8_t version[4] = {1, 4, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
+ env, TAG_DNGVERSION);
+
+ uint8_t backwardVersion[4] = {1, 1, 0, 0};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
+ env, TAG_DNGBACKWARDVERSION);
+ }
+
+ {
+ // Set whitelevel
+ camera_metadata_entry entry =
+ characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
+ BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL);
+ uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
+ BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
+ TAG_WHITELEVEL);
+ }
+
+ {
+ // Set default scale
+ uint32_t defaultScale[4] = {1, 1, 1, 1};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
+ env, TAG_DEFAULTSCALE);
+ }
+
+ bool singleIlluminant = false;
+ {
+ // Set calibration illuminants
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1);
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
+ if (entry2.count == 0) {
+ singleIlluminant = true;
+ }
+ uint16_t ref1 = entry1.data.u8[0];
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1);
+
+ if (!singleIlluminant) {
+ uint16_t ref2 = entry2.data.u8[0];
+ BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
+ TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2);
+ }
+ }
+
+ {
+ // Set color transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1);
+
+ int32_t colorTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ colorTransform1[ctr++] = entry1.data.r[i].numerator;
+ colorTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, TIFF_IFD_0),
+ env, TAG_COLORMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2);
+ int32_t colorTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ colorTransform2[ctr++] = entry2.data.r[i].numerator;
+ colorTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, TIFF_IFD_0),
+ env, TAG_COLORMATRIX2);
+ }
+ }
+
+ {
+ // Set calibration transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
+ BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1);
+
+ int32_t calibrationTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
+ calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
+ BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2);
+ int32_t calibrationTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
+ calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
+ TIFF_IFD_0), env, TAG_CAMERACALIBRATION2);
+ }
+ }
+
+ {
+ // Set forward transforms
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
+ BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1);
+
+ int32_t forwardTransform1[entry1.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry1.count; ++i) {
+ forwardTransform1[ctr++] = entry1.data.r[i].numerator;
+ forwardTransform1[ctr++] = entry1.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX1);
+
+ if (!singleIlluminant) {
+ camera_metadata_entry entry2 =
+ characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
+ BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2);
+ int32_t forwardTransform2[entry2.count * 2];
+
+ ctr = 0;
+ for(size_t i = 0; i < entry2.count; ++i) {
+ forwardTransform2[ctr++] = entry2.data.r[i].numerator;
+ forwardTransform2[ctr++] = entry2.data.r[i].denominator;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
+ TIFF_IFD_0), env, TAG_FORWARDMATRIX2);
+ }
+ }
+
+ {
+ // Set camera neutral
+ camera_metadata_entry entry =
+ results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
+ BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL);
+ uint32_t cameraNeutral[entry.count * 2];
+
+ size_t ctr = 0;
+ for(size_t i = 0; i < entry.count; ++i) {
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].numerator);
+ cameraNeutral[ctr++] =
+ static_cast<uint32_t>(entry.data.r[i].denominator);
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
+ TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL);
+ }
+
+ {
+ // Setup data strips
+ // TODO: Switch to tiled implementation.
+ uint32_t offset = 0;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &offset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_ROWSPERSTRIP, 1, &imageHeight, TIFF_IFD_0), env,
+ TAG_ROWSPERSTRIP);
+
+ uint32_t byteCount = imageWidth * imageHeight * bitsPerSample * samplesPerPixel /
+ bitsPerByte;
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPBYTECOUNTS, 1, &byteCount, TIFF_IFD_0), env,
+ TAG_STRIPBYTECOUNTS);
+ }
+
+ {
+ // Setup default crop + crop origin tags
+ uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
+ uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
+ if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
+ uint32_t defaultCropOrigin[] = {margin, margin};
+ uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN);
+ BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
+ TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE);
+ }
+ }
+
+ {
+ // Setup unique camera model tag
+ char model[PROPERTY_VALUE_MAX];
+ property_get("ro.product.model", model, "");
+
+ char manufacturer[PROPERTY_VALUE_MAX];
+ property_get("ro.product.manufacturer", manufacturer, "");
+
+ char brand[PROPERTY_VALUE_MAX];
+ property_get("ro.product.brand", brand, "");
+
+ String8 cameraModel(model);
+ cameraModel += "-";
+ cameraModel += manufacturer;
+ cameraModel += "-";
+ cameraModel += brand;
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
+ reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
+ TAG_UNIQUECAMERAMODEL);
+ }
+
+ {
+ // Setup opcode List 2
+ camera_metadata_entry entry1 =
+ characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
+ BAIL_IF_EMPTY(entry1, env, TAG_OPCODELIST2);
+ uint32_t lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
+ uint32_t lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
+
+ camera_metadata_entry entry2 =
+ results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ BAIL_IF_EMPTY(entry2, env, TAG_OPCODELIST2);
+ if (entry2.count == lsmWidth * lsmHeight * 4) {
+
+ OpcodeListBuilder builder;
+ status_t err = builder.addGainMapsForMetadata(lsmWidth,
+ lsmHeight,
+ 0,
+ 0,
+ imageHeight,
+ imageWidth,
+ opcodeCfaLayout,
+ entry2.data.f);
+ if (err == OK) {
+ size_t listSize = builder.getSize();
+ uint8_t opcodeListBuf[listSize];
+ err = builder.buildOpList(opcodeListBuf);
+ if (err == OK) {
+ BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
+ TIFF_IFD_0), env, TAG_OPCODELIST2);
+ } else {
+ ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
+ }
+ } else {
+ ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add lens shading map.");
+ }
+ } else {
+ ALOGW("%s: Lens shading map not present in results, skipping...", __FUNCTION__);
+ }
+ }
+
+ DngCreator_setCreator(env, thiz, writer);
+}
+
+static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ DngCreator_setCreator(env, thiz, NULL);
+}
+
+static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetOrientation is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailBitmap(JNIEnv* env, jobject thiz, jobject bitmap) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailBitmap is not implemented");
+}
+
+static void DngCreator_nativeSetThumbnailImage(JNIEnv* env, jobject thiz, jint width, jint height,
+ jobject yBuffer, jint yRowStride, jint yPixStride, jobject uBuffer, jint uRowStride,
+ jint uPixStride, jobject vBuffer, jint vRowStride, jint vPixStride) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeSetThumbnailImage is not implemented");
+}
+
+static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
+ jint height, jobject inBuffer, jint rowStride, jint pixStride) {
+ ALOGV("%s:", __FUNCTION__);
+
+ sp<JniOutputStream> out = new JniOutputStream(env, outStream);
+ if(env->ExceptionCheck()) {
+ ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
+ return;
+ }
+
+ uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
+ if (pixelBytes == NULL) {
+ ALOGE("%s: Could not get native byte buffer", __FUNCTION__);
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid bytebuffer");
+ return;
+ }
+
+ TiffWriter* writer = DngCreator_getCreator(env, thiz);
+ if (writer == NULL) {
+ ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
+ jniThrowException(env, "java/lang/AssertionError",
+ "Write called with uninitialized DngCreator");
+ return;
+ }
+ // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
+ uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, TIFF_IFD_0)->getData<uint32_t>());
+ uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, TIFF_IFD_0)->getData<uint32_t>());
+ if (metadataWidth != width) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata width %d doesn't match image width %d", metadataWidth, width);
+ return;
+ }
+
+ if (metadataHeight != height) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException", \
+ "Metadata height %d doesn't match image height %d", metadataHeight, height);
+ return;
+ }
+
+ uint32_t stripOffset = writer->getTotalSize();
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_STRIPOFFSETS, 1, &stripOffset, TIFF_IFD_0), env,
+ TAG_STRIPOFFSETS);
+
+ if (writer->write(out.get()) != OK) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write metadata");
+ }
+ return;
+ }
+
+ size_t fullSize = rowStride * height;
+ jlong capacity = env->GetDirectBufferCapacity(inBuffer);
+ if (capacity < 0 || fullSize > capacity) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Invalid size %d for Image, size given in metadata is %d at current stride",
+ capacity, fullSize);
+ return;
+ }
+
+ if (pixStride == BYTES_PER_SAMPLE && rowStride == width * BYTES_PER_SAMPLE) {
+ if (out->write(pixelBytes, 0, fullSize) != OK || env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ } else if (pixStride == BYTES_PER_SAMPLE) {
+ for (size_t i = 0; i < height; ++i) {
+ if (out->write(pixelBytes, i * rowStride, pixStride * width) != OK ||
+ env->ExceptionCheck()) {
+ if (!env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ } else {
+ for (size_t i = 0; i < height; ++i) {
+ for (size_t j = 0; j < width; ++j) {
+ if (out->write(pixelBytes, i * rowStride + j * pixStride,
+ BYTES_PER_SAMPLE) != OK || !env->ExceptionCheck()) {
+ if (env->ExceptionCheck()) {
+ jniThrowException(env, "java/io/IOException", "Failed to write pixel data");
+ }
+ return;
+ }
+ }
+ }
+ }
+
+}
+
+static void DngCreator_nativeWriteByteBuffer(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject rawBuffer, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteByteBuffer is not implemented.");
+}
+
+static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
+ jobject inStream, jlong offset) {
+ ALOGV("%s:", __FUNCTION__);
+ jniThrowRuntimeException(env, "nativeWriteInputStream is not implemented.");
+}
+
+} /*extern "C" */
+
+static JNINativeMethod gDngCreatorMethods[] = {
+ {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
+ {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
+ "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
+ {"nativeDestroy", "()V", (void*) DngCreator_destroy},
+ {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
+ {"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
+ (void*) DngCreator_nativeSetThumbnailBitmap},
+ {"nativeSetThumbnailImage",
+ "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeSetThumbnailImage},
+ {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;II)V",
+ (void*) DngCreator_nativeWriteImage},
+ {"nativeWriteByteBuffer", "(Ljava/io/OutputStream;Ljava/nio/ByteBuffer;J)V",
+ (void*) DngCreator_nativeWriteByteBuffer},
+ {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;J)V",
+ (void*) DngCreator_nativeWriteInputStream},
+};
+
+int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/hardware/camera2/DngCreator", gDngCreatorMethods,
+ NELEM(gDngCreatorMethods));
+}