Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "DngCreator_JNI"
     19 
     20 #include <system/camera_metadata.h>
     21 #include <camera/CameraMetadata.h>
     22 #include <img_utils/DngUtils.h>
     23 #include <img_utils/TagDefinitions.h>
     24 #include <img_utils/TiffIfd.h>
     25 #include <img_utils/TiffWriter.h>
     26 #include <img_utils/Output.h>
     27 #include <img_utils/Input.h>
     28 #include <img_utils/StripSource.h>
     29 
     30 #include <utils/Log.h>
     31 #include <utils/Errors.h>
     32 #include <utils/StrongPointer.h>
     33 #include <utils/RefBase.h>
     34 #include <utils/Vector.h>
     35 #include <cutils/properties.h>
     36 
     37 #include <string.h>
     38 #include <inttypes.h>
     39 
     40 #include "android_runtime/AndroidRuntime.h"
     41 #include "android_runtime/android_hardware_camera2_CameraMetadata.h"
     42 
     43 #include <jni.h>
     44 #include <JNIHelp.h>
     45 
     46 using namespace android;
     47 using namespace img_utils;
     48 
     49 #define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
     50     if ((expr) != OK) { \
     51         jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
     52                 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
     53         return; \
     54     }
     55 
     56 #define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
     57     if (entry.count == 0) { \
     58         jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
     59                 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
     60         return; \
     61     }
     62 
     63 #define ANDROID_DNGCREATOR_CTX_JNI_ID     "mNativeContext"
     64 
     65 static struct {
     66     jfieldID mNativeContext;
     67 } gDngCreatorClassInfo;
     68 
     69 static struct {
     70     jmethodID mWriteMethod;
     71 } gOutputStreamClassInfo;
     72 
     73 static struct {
     74     jmethodID mReadMethod;
     75     jmethodID mSkipMethod;
     76 } gInputStreamClassInfo;
     77 
     78 static struct {
     79     jmethodID mGetMethod;
     80 } gInputByteBufferClassInfo;
     81 
     82 enum {
     83     BITS_PER_SAMPLE = 16,
     84     BYTES_PER_SAMPLE = 2,
     85     BYTES_PER_RGB_PIXEL = 3,
     86     BITS_PER_RGB_SAMPLE = 8,
     87     BYTES_PER_RGB_SAMPLE = 1,
     88     SAMPLES_PER_RGB_PIXEL = 3,
     89     SAMPLES_PER_RAW_PIXEL = 1,
     90     TIFF_IFD_0 = 0,
     91     TIFF_IFD_SUB1 = 1,
     92     TIFF_IFD_GPSINFO = 2,
     93 };
     94 
     95 // ----------------------------------------------------------------------------
     96 
     97 /**
     98  * Container class for the persistent native context.
     99  */
    100 
    101 class NativeContext : public LightRefBase<NativeContext> {
    102 
    103 public:
    104     NativeContext();
    105     virtual ~NativeContext();
    106 
    107     TiffWriter* getWriter();
    108 
    109     uint32_t getThumbnailWidth();
    110     uint32_t getThumbnailHeight();
    111     const uint8_t* getThumbnail();
    112 
    113     bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
    114 
    115 private:
    116     Vector<uint8_t> mCurrentThumbnail;
    117     TiffWriter mWriter;
    118     uint32_t mThumbnailWidth;
    119     uint32_t mThumbnailHeight;
    120 };
    121 
    122 NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
    123 
    124 NativeContext::~NativeContext() {}
    125 
    126 TiffWriter* NativeContext::getWriter() {
    127     return &mWriter;
    128 }
    129 
    130 uint32_t NativeContext::getThumbnailWidth() {
    131     return mThumbnailWidth;
    132 }
    133 
    134 uint32_t NativeContext::getThumbnailHeight() {
    135     return mThumbnailHeight;
    136 }
    137 
    138 const uint8_t* NativeContext::getThumbnail() {
    139     return mCurrentThumbnail.array();
    140 }
    141 
    142 bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
    143     mThumbnailWidth = width;
    144     mThumbnailHeight = height;
    145 
    146     size_t size = BYTES_PER_RGB_PIXEL * width * height;
    147     if (mCurrentThumbnail.resize(size) < 0) {
    148         ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
    149         return false;
    150     }
    151 
    152     uint8_t* thumb = mCurrentThumbnail.editArray();
    153     memcpy(thumb, buffer, size);
    154     return true;
    155 }
    156 
    157 // End of NativeContext
    158 // ----------------------------------------------------------------------------
    159 
    160 /**
    161  * Wrapper class for a Java OutputStream.
    162  *
    163  * This class is not intended to be used across JNI calls.
    164  */
    165 class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
    166 public:
    167     JniOutputStream(JNIEnv* env, jobject outStream);
    168 
    169     virtual ~JniOutputStream();
    170 
    171     status_t open();
    172 
    173     status_t write(const uint8_t* buf, size_t offset, size_t count);
    174 
    175     status_t close();
    176 private:
    177     enum {
    178         BYTE_ARRAY_LENGTH = 4096
    179     };
    180     jobject mOutputStream;
    181     JNIEnv* mEnv;
    182     jbyteArray mByteArray;
    183 };
    184 
    185 JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
    186         mEnv(env) {
    187     mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
    188     if (mByteArray == NULL) {
    189         jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
    190     }
    191 }
    192 
    193 JniOutputStream::~JniOutputStream() {
    194     mEnv->DeleteLocalRef(mByteArray);
    195 }
    196 
    197 status_t JniOutputStream::open() {
    198     // Do nothing
    199     return OK;
    200 }
    201 
    202 status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
    203     while(count > 0) {
    204         size_t len = BYTE_ARRAY_LENGTH;
    205         len = (count > len) ? len : count;
    206         mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
    207 
    208         if (mEnv->ExceptionCheck()) {
    209             return BAD_VALUE;
    210         }
    211 
    212         mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
    213                 0, len);
    214 
    215         if (mEnv->ExceptionCheck()) {
    216             return BAD_VALUE;
    217         }
    218 
    219         count -= len;
    220         offset += len;
    221     }
    222     return OK;
    223 }
    224 
    225 status_t JniOutputStream::close() {
    226     // Do nothing
    227     return OK;
    228 }
    229 
    230 // End of JniOutputStream
    231 // ----------------------------------------------------------------------------
    232 
    233 /**
    234  * Wrapper class for a Java InputStream.
    235  *
    236  * This class is not intended to be used across JNI calls.
    237  */
    238 class JniInputStream : public Input, public LightRefBase<JniInputStream> {
    239 public:
    240     JniInputStream(JNIEnv* env, jobject inStream);
    241 
    242     status_t open();
    243 
    244     status_t close();
    245 
    246     ssize_t read(uint8_t* buf, size_t offset, size_t count);
    247 
    248     ssize_t skip(size_t count);
    249 
    250     virtual ~JniInputStream();
    251 private:
    252     enum {
    253         BYTE_ARRAY_LENGTH = 4096
    254     };
    255     jobject mInStream;
    256     JNIEnv* mEnv;
    257     jbyteArray mByteArray;
    258 
    259 };
    260 
    261 JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
    262     mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
    263     if (mByteArray == NULL) {
    264         jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
    265     }
    266 }
    267 
    268 JniInputStream::~JniInputStream() {
    269     mEnv->DeleteLocalRef(mByteArray);
    270 }
    271 
    272 ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
    273 
    274     jint realCount = BYTE_ARRAY_LENGTH;
    275     if (count < BYTE_ARRAY_LENGTH) {
    276         realCount = count;
    277     }
    278     jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
    279             realCount);
    280 
    281     if (actual < 0) {
    282         return NOT_ENOUGH_DATA;
    283     }
    284 
    285     if (mEnv->ExceptionCheck()) {
    286         return BAD_VALUE;
    287     }
    288 
    289     mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
    290     if (mEnv->ExceptionCheck()) {
    291         return BAD_VALUE;
    292     }
    293     return actual;
    294 }
    295 
    296 ssize_t JniInputStream::skip(size_t count) {
    297     jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
    298             static_cast<jlong>(count));
    299 
    300     if (mEnv->ExceptionCheck()) {
    301         return BAD_VALUE;
    302     }
    303     if (actual < 0) {
    304         return NOT_ENOUGH_DATA;
    305     }
    306     return actual;
    307 }
    308 
    309 status_t JniInputStream::open() {
    310     // Do nothing
    311     return OK;
    312 }
    313 
    314 status_t JniInputStream::close() {
    315     // Do nothing
    316     return OK;
    317 }
    318 
    319 // End of JniInputStream
    320 // ----------------------------------------------------------------------------
    321 
    322 /**
    323  * Wrapper class for a non-direct Java ByteBuffer.
    324  *
    325  * This class is not intended to be used across JNI calls.
    326  */
    327 class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
    328 public:
    329     JniInputByteBuffer(JNIEnv* env, jobject inBuf);
    330 
    331     status_t open();
    332 
    333     status_t close();
    334 
    335     ssize_t read(uint8_t* buf, size_t offset, size_t count);
    336 
    337     virtual ~JniInputByteBuffer();
    338 private:
    339     enum {
    340         BYTE_ARRAY_LENGTH = 4096
    341     };
    342     jobject mInBuf;
    343     JNIEnv* mEnv;
    344     jbyteArray mByteArray;
    345 };
    346 
    347 JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
    348     mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
    349     if (mByteArray == NULL) {
    350         jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
    351     }
    352 }
    353 
    354 JniInputByteBuffer::~JniInputByteBuffer() {
    355     mEnv->DeleteLocalRef(mByteArray);
    356 }
    357 
    358 ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
    359     jint realCount = BYTE_ARRAY_LENGTH;
    360     if (count < BYTE_ARRAY_LENGTH) {
    361         realCount = count;
    362     }
    363 
    364     jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
    365             realCount);
    366     mEnv->DeleteLocalRef(chainingBuf);
    367 
    368     if (mEnv->ExceptionCheck()) {
    369         ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
    370         return BAD_VALUE;
    371     }
    372 
    373     mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
    374     if (mEnv->ExceptionCheck()) {
    375         ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
    376         return BAD_VALUE;
    377     }
    378     return realCount;
    379 }
    380 
    381 status_t JniInputByteBuffer::open() {
    382     // Do nothing
    383     return OK;
    384 }
    385 
    386 status_t JniInputByteBuffer::close() {
    387     // Do nothing
    388     return OK;
    389 }
    390 
    391 // End of JniInputByteBuffer
    392 // ----------------------------------------------------------------------------
    393 
    394 /**
    395  * StripSource subclass for Input types.
    396  *
    397  * This class is not intended to be used across JNI calls.
    398  */
    399 
    400 class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
    401 public:
    402     InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
    403             uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
    404             uint32_t samplesPerPixel);
    405 
    406     virtual ~InputStripSource();
    407 
    408     virtual status_t writeToStream(Output& stream, uint32_t count);
    409 
    410     virtual uint32_t getIfd() const;
    411 protected:
    412     uint32_t mIfd;
    413     Input* mInput;
    414     uint32_t mWidth;
    415     uint32_t mHeight;
    416     uint32_t mPixStride;
    417     uint32_t mRowStride;
    418     uint64_t mOffset;
    419     JNIEnv* mEnv;
    420     uint32_t mBytesPerSample;
    421     uint32_t mSamplesPerPixel;
    422 };
    423 
    424 InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
    425         uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
    426         uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
    427         mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
    428         mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
    429         mSamplesPerPixel(samplesPerPixel) {}
    430 
    431 InputStripSource::~InputStripSource() {}
    432 
    433 status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
    434     status_t err = OK;
    435     uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
    436     jlong offset = mOffset;
    437 
    438     if (fullSize != count) {
    439         ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
    440                 fullSize);
    441         jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
    442         return BAD_VALUE;
    443     }
    444 
    445     // Skip offset
    446     while (offset > 0) {
    447         ssize_t skipped = mInput->skip(offset);
    448         if (skipped <= 0) {
    449             if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
    450                 jniThrowExceptionFmt(mEnv, "java/io/IOException",
    451                         "Early EOF encountered in skip, not enough pixel data for image of size %u",
    452                         fullSize);
    453                 skipped = NOT_ENOUGH_DATA;
    454             } else {
    455                 if (!mEnv->ExceptionCheck()) {
    456                     jniThrowException(mEnv, "java/io/IOException",
    457                             "Error encountered while skip bytes in input stream.");
    458                 }
    459             }
    460 
    461             return skipped;
    462         }
    463         offset -= skipped;
    464     }
    465 
    466     Vector<uint8_t> row;
    467     if (row.resize(mRowStride) < 0) {
    468         jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
    469         return BAD_VALUE;
    470     }
    471 
    472     uint8_t* rowBytes = row.editArray();
    473 
    474     for (uint32_t i = 0; i < mHeight; ++i) {
    475         size_t rowFillAmt = 0;
    476         size_t rowSize = mRowStride;
    477 
    478         while (rowFillAmt < mRowStride) {
    479             ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
    480             if (bytesRead <= 0) {
    481                 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
    482                     ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
    483                             __FUNCTION__, i, bytesRead);
    484                     jniThrowExceptionFmt(mEnv, "java/io/IOException",
    485                             "Early EOF encountered, not enough pixel data for image of size %"
    486                             PRIu32, fullSize);
    487                     bytesRead = NOT_ENOUGH_DATA;
    488                 } else {
    489                     if (!mEnv->ExceptionCheck()) {
    490                         jniThrowException(mEnv, "java/io/IOException",
    491                                 "Error encountered while reading");
    492                     }
    493                 }
    494                 return bytesRead;
    495             }
    496             rowFillAmt += bytesRead;
    497             rowSize -= bytesRead;
    498         }
    499 
    500         if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
    501             ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
    502 
    503             if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
    504                     mEnv->ExceptionCheck()) {
    505                 if (!mEnv->ExceptionCheck()) {
    506                     jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
    507                 }
    508                 return BAD_VALUE;
    509             }
    510         } else {
    511             ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
    512             jniThrowException(mEnv, "java/lang/IllegalStateException",
    513                     "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
    514             return BAD_VALUE;
    515 
    516             // TODO: Add support for non-contiguous pixels if needed.
    517         }
    518     }
    519     return OK;
    520 }
    521 
    522 uint32_t InputStripSource::getIfd() const {
    523     return mIfd;
    524 }
    525 
    526 // End of InputStripSource
    527 // ----------------------------------------------------------------------------
    528 
    529 /**
    530  * StripSource subclass for direct buffer types.
    531  *
    532  * This class is not intended to be used across JNI calls.
    533  */
    534 
    535 class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
    536 public:
    537     DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
    538             uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
    539             uint32_t bytesPerSample, uint32_t samplesPerPixel);
    540 
    541     virtual ~DirectStripSource();
    542 
    543     virtual status_t writeToStream(Output& stream, uint32_t count);
    544 
    545     virtual uint32_t getIfd() const;
    546 protected:
    547     uint32_t mIfd;
    548     const uint8_t* mPixelBytes;
    549     uint32_t mWidth;
    550     uint32_t mHeight;
    551     uint32_t mPixStride;
    552     uint32_t mRowStride;
    553     uint16_t mOffset;
    554     JNIEnv* mEnv;
    555     uint32_t mBytesPerSample;
    556     uint32_t mSamplesPerPixel;
    557 };
    558 
    559 DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
    560             uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
    561             uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
    562             mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
    563             mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
    564             mSamplesPerPixel(samplesPerPixel) {}
    565 
    566 DirectStripSource::~DirectStripSource() {}
    567 
    568 status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
    569     uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
    570 
    571     if (fullSize != count) {
    572         ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
    573                 fullSize);
    574         jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
    575         return BAD_VALUE;
    576     }
    577 
    578     if (mPixStride == mBytesPerSample * mSamplesPerPixel
    579             && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
    580         ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
    581 
    582         if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
    583             if (!mEnv->ExceptionCheck()) {
    584                 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
    585             }
    586             return BAD_VALUE;
    587         }
    588     } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
    589         ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
    590 
    591         for (size_t i = 0; i < mHeight; ++i) {
    592             if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
    593                         mEnv->ExceptionCheck()) {
    594                 if (!mEnv->ExceptionCheck()) {
    595                     jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
    596                 }
    597                 return BAD_VALUE;
    598             }
    599         }
    600     } else {
    601         ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
    602 
    603         jniThrowException(mEnv, "java/lang/IllegalStateException",
    604                 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
    605         return BAD_VALUE;
    606 
    607         // TODO: Add support for non-contiguous pixels if needed.
    608     }
    609     return OK;
    610 
    611 }
    612 
    613 uint32_t DirectStripSource::getIfd() const {
    614     return mIfd;
    615 }
    616 
    617 // End of DirectStripSource
    618 // ----------------------------------------------------------------------------
    619 
    620 static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
    621     bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
    622 
    623     // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
    624     uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
    625     uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
    626 
    627     if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
    628         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
    629                         "Metadata width %d doesn't match image width %d", metadataWidth, width);
    630         return false;
    631     }
    632 
    633     if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
    634         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
    635                         "Metadata height %d doesn't match image height %d", metadataHeight, height);
    636         return false;
    637     }
    638 
    639     return true;
    640 }
    641 
    642 static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
    643         const Vector<uint16_t>& entries) {
    644     for (size_t i = 0; i < entries.size(); ++i) {
    645         uint16_t tagId = entries[i];
    646         sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
    647         if (entry == NULL) {
    648             ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
    649                     ifdFrom);
    650             return BAD_VALUE;
    651         }
    652         if (writer->addEntry(entry, ifdTo) != OK) {
    653             ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
    654                     ifdFrom);
    655             return BAD_VALUE;
    656         }
    657         writer->removeEntry(tagId, ifdFrom);
    658     }
    659     return OK;
    660 }
    661 
    662 /**
    663  * Write CFA pattern for given CFA enum into cfaOut.  cfaOut must have length >= 4.
    664  * Returns OK on success, or a negative error code if the CFA enum was invalid.
    665  */
    666 static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
    667     camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
    668             static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
    669             cfaEnum);
    670     switch(cfa) {
    671         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
    672             cfaOut[0] = 0;
    673             cfaOut[1] = 1;
    674             cfaOut[2] = 1;
    675             cfaOut[3] = 2;
    676             break;
    677         }
    678         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
    679             cfaOut[0] = 1;
    680             cfaOut[1] = 0;
    681             cfaOut[2] = 2;
    682             cfaOut[3] = 1;
    683             break;
    684         }
    685         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
    686             cfaOut[0] = 1;
    687             cfaOut[1] = 2;
    688             cfaOut[2] = 0;
    689             cfaOut[3] = 1;
    690             break;
    691         }
    692         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
    693             cfaOut[0] = 2;
    694             cfaOut[1] = 1;
    695             cfaOut[2] = 1;
    696             cfaOut[3] = 0;
    697             break;
    698         }
    699         default: {
    700             return BAD_VALUE;
    701         }
    702     }
    703     return OK;
    704 }
    705 
    706 /**
    707  * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
    708  * RGGB for an unknown enum.
    709  */
    710 static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
    711     camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
    712             static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
    713             cfaEnum);
    714     switch(cfa) {
    715         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
    716             return OpcodeListBuilder::CFA_RGGB;
    717         }
    718         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
    719             return OpcodeListBuilder::CFA_GRBG;
    720         }
    721         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
    722             return OpcodeListBuilder::CFA_GBRG;
    723         }
    724         case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
    725             return OpcodeListBuilder::CFA_BGGR;
    726         }
    727         default: {
    728             return OpcodeListBuilder::CFA_RGGB;
    729         }
    730     }
    731 }
    732 
    733 /**
    734  * For each color plane, find the corresponding noise profile coefficients given in the
    735  * per-channel noise profile.  If multiple channels in the CFA correspond to a color in the color
    736  * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
    737  *
    738  * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
    739  * cfa - numChannels color channels corresponding to each of the per-channel noise profile
    740  *       coefficients.
    741  * numChannels - the number of noise profile coefficient pairs and color channels given in
    742  *       the perChannelNoiseProfile and cfa arguments, respectively.
    743  * planeColors - the color planes in the noise profile output.
    744  * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
    745  * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
    746  *
    747  * returns OK, or a negative error code on failure.
    748  */
    749 static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
    750         size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
    751         /*out*/double* noiseProfile) {
    752 
    753     for (size_t p = 0; p < numPlanes; ++p) {
    754         size_t S = p * 2;
    755         size_t O = p * 2 + 1;
    756 
    757         noiseProfile[S] = 0;
    758         noiseProfile[O] = 0;
    759         bool uninitialized = true;
    760         for (size_t c = 0; c < numChannels; ++c) {
    761             if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
    762                 noiseProfile[S] = perChannelNoiseProfile[c * 2];
    763                 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
    764                 uninitialized = false;
    765             }
    766         }
    767         if (uninitialized) {
    768             ALOGE("%s: No valid NoiseProfile coefficients for color plane %u", __FUNCTION__, p);
    769             return BAD_VALUE;
    770         }
    771     }
    772     return OK;
    773 }
    774 
    775 // ----------------------------------------------------------------------------
    776 extern "C" {
    777 
    778 static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
    779     ALOGV("%s:", __FUNCTION__);
    780     return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
    781             gDngCreatorClassInfo.mNativeContext));
    782 }
    783 
    784 static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
    785     ALOGV("%s:", __FUNCTION__);
    786     NativeContext* current = DngCreator_getNativeContext(env, thiz);
    787 
    788     if (context != NULL) {
    789         context->incStrong((void*) DngCreator_setNativeContext);
    790     }
    791 
    792     if (current) {
    793         current->decStrong((void*) DngCreator_setNativeContext);
    794     }
    795 
    796     env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
    797             reinterpret_cast<jlong>(context.get()));
    798 }
    799 
    800 static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
    801     ALOGV("%s:", __FUNCTION__);
    802     NativeContext* current = DngCreator_getNativeContext(env, thiz);
    803     if (current) {
    804         return current->getWriter();
    805     }
    806     return NULL;
    807 }
    808 
    809 static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
    810     ALOGV("%s:", __FUNCTION__);
    811 
    812     gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
    813             ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
    814     LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
    815             "can't find android/hardware/camera2/DngCreator.%s",
    816             ANDROID_DNGCREATOR_CTX_JNI_ID);
    817 
    818     jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
    819     LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
    820     gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
    821     LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
    822 
    823     jclass inputStreamClazz = env->FindClass("java/io/InputStream");
    824     LOG_ALWAYS_FATAL_IF(inputStreamClazz == NULL, "Can't find java/io/InputStream class");
    825     gInputStreamClassInfo.mReadMethod = env->GetMethodID(inputStreamClazz, "read", "([BII)I");
    826     LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mReadMethod == NULL, "Can't find read method");
    827     gInputStreamClassInfo.mSkipMethod = env->GetMethodID(inputStreamClazz, "skip", "(J)J");
    828     LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mSkipMethod == NULL, "Can't find skip method");
    829 
    830     jclass inputBufferClazz = env->FindClass("java/nio/ByteBuffer");
    831     LOG_ALWAYS_FATAL_IF(inputBufferClazz == NULL, "Can't find java/nio/ByteBuffer class");
    832     gInputByteBufferClassInfo.mGetMethod = env->GetMethodID(inputBufferClazz, "get",
    833             "([BII)Ljava/nio/ByteBuffer;");
    834     LOG_ALWAYS_FATAL_IF(gInputByteBufferClassInfo.mGetMethod == NULL, "Can't find get method");
    835 }
    836 
    837 static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
    838         jobject resultsPtr, jstring formattedCaptureTime) {
    839     ALOGV("%s:", __FUNCTION__);
    840     CameraMetadata characteristics;
    841     CameraMetadata results;
    842     if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
    843          jniThrowException(env, "java/lang/AssertionError",
    844                 "No native metadata defined for camera characteristics.");
    845          return;
    846     }
    847     if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
    848         jniThrowException(env, "java/lang/AssertionError",
    849                 "No native metadata defined for capture results.");
    850         return;
    851     }
    852 
    853     sp<NativeContext> nativeContext = new NativeContext();
    854     TiffWriter* writer = nativeContext->getWriter();
    855 
    856     writer->addIfd(TIFF_IFD_0);
    857 
    858     status_t err = OK;
    859 
    860     const uint32_t samplesPerPixel = 1;
    861     const uint32_t bitsPerSample = BITS_PER_SAMPLE;
    862     const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
    863     uint32_t imageWidth = 0;
    864     uint32_t imageHeight = 0;
    865 
    866     OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
    867     uint8_t cfaPlaneColor[3] = {0, 1, 2};
    868     uint8_t cfaEnum = -1;
    869 
    870     // TODO: Greensplit.
    871     // TODO: Add remaining non-essential tags
    872 
    873     // Setup main image tags
    874 
    875     {
    876         // Set orientation
    877         uint16_t orientation = 1; // Normal
    878         BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
    879                 TAG_ORIENTATION, writer);
    880     }
    881 
    882     {
    883         // Set subfiletype
    884         uint32_t subfileType = 0; // Main image
    885         BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
    886                 TAG_NEWSUBFILETYPE, writer);
    887     }
    888 
    889     {
    890         // Set bits per sample
    891         uint16_t bits = static_cast<uint16_t>(bitsPerSample);
    892         BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
    893                 TAG_BITSPERSAMPLE, writer);
    894     }
    895 
    896     {
    897         // Set compression
    898         uint16_t compression = 1; // None
    899         BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
    900                 TAG_COMPRESSION, writer);
    901     }
    902 
    903     {
    904         // Set dimensions
    905         camera_metadata_entry entry =
    906                 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
    907         BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
    908         uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
    909         uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
    910         BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
    911                 TAG_IMAGEWIDTH, writer);
    912         BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
    913                 TAG_IMAGELENGTH, writer);
    914         imageWidth = width;
    915         imageHeight = height;
    916     }
    917 
    918     {
    919         // Set photometric interpretation
    920         uint16_t interpretation = 32803; // CFA
    921         BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
    922                 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
    923     }
    924 
    925     {
    926         // Set blacklevel tags
    927         camera_metadata_entry entry =
    928                 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
    929         BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
    930         const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
    931         BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
    932                 TAG_BLACKLEVEL, writer);
    933 
    934         uint16_t repeatDim[2] = {2, 2};
    935         BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
    936                 TAG_BLACKLEVELREPEATDIM, writer);
    937     }
    938 
    939     {
    940         // Set samples per pixel
    941         uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
    942         BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
    943                 env, TAG_SAMPLESPERPIXEL, writer);
    944     }
    945 
    946     {
    947         // Set planar configuration
    948         uint16_t config = 1; // Chunky
    949         BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
    950                 env, TAG_PLANARCONFIGURATION, writer);
    951     }
    952 
    953     {
    954         // Set CFA pattern dimensions
    955         uint16_t repeatDim[2] = {2, 2};
    956         BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
    957                 env, TAG_CFAREPEATPATTERNDIM, writer);
    958     }
    959 
    960     {
    961         // Set CFA pattern
    962         camera_metadata_entry entry =
    963                         characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
    964         BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
    965 
    966         const int cfaLength = 4;
    967         cfaEnum = entry.data.u8[0];
    968         uint8_t cfa[cfaLength];
    969         if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
    970             jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
    971                         "Invalid metadata for tag %d", TAG_CFAPATTERN);
    972         }
    973 
    974         BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
    975                 TAG_CFAPATTERN, writer);
    976 
    977         opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
    978     }
    979 
    980     {
    981         // Set CFA plane color
    982         BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
    983                 env, TAG_CFAPLANECOLOR, writer);
    984     }
    985 
    986     {
    987         // Set CFA layout
    988         uint16_t cfaLayout = 1;
    989         BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
    990                 env, TAG_CFALAYOUT, writer);
    991     }
    992 
    993     {
    994         // image description
    995         uint8_t imageDescription = '\0'; // empty
    996         BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
    997                 env, TAG_IMAGEDESCRIPTION, writer);
    998     }
    999 
   1000     {
   1001         // make
   1002         char manufacturer[PROPERTY_VALUE_MAX];
   1003 
   1004         // Use "" to represent unknown make as suggested in TIFF/EP spec.
   1005         property_get("ro.product.manufacturer", manufacturer, "");
   1006         uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
   1007 
   1008         BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
   1009                 TIFF_IFD_0), env, TAG_MAKE, writer);
   1010     }
   1011 
   1012     {
   1013         // model
   1014         char model[PROPERTY_VALUE_MAX];
   1015 
   1016         // Use "" to represent unknown model as suggested in TIFF/EP spec.
   1017         property_get("ro.product.model", model, "");
   1018         uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
   1019 
   1020         BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
   1021                 TIFF_IFD_0), env, TAG_MODEL, writer);
   1022     }
   1023 
   1024     {
   1025         // x resolution
   1026         uint32_t xres[] = { 72, 1 }; // default 72 ppi
   1027         BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
   1028                 env, TAG_XRESOLUTION, writer);
   1029 
   1030         // y resolution
   1031         uint32_t yres[] = { 72, 1 }; // default 72 ppi
   1032         BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
   1033                 env, TAG_YRESOLUTION, writer);
   1034 
   1035         uint16_t unit = 2; // inches
   1036         BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
   1037                 env, TAG_RESOLUTIONUNIT, writer);
   1038     }
   1039 
   1040     {
   1041         // software
   1042         char software[PROPERTY_VALUE_MAX];
   1043         property_get("ro.build.fingerprint", software, "");
   1044         uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
   1045         BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
   1046                 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
   1047     }
   1048 
   1049     {
   1050         // datetime
   1051         const size_t DATETIME_COUNT = 20;
   1052         const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
   1053 
   1054         size_t len = strlen(captureTime) + 1;
   1055         if (len != DATETIME_COUNT) {
   1056             jniThrowException(env, "java/lang/IllegalArgumentException",
   1057                     "Timestamp string length is not required 20 characters");
   1058             return;
   1059         }
   1060 
   1061         if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
   1062                 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
   1063             env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
   1064             jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
   1065                     "Invalid metadata for tag %x", TAG_DATETIME);
   1066             return;
   1067         }
   1068 
   1069         // datetime original
   1070         if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
   1071                 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
   1072             env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
   1073             jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
   1074                     "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
   1075             return;
   1076         }
   1077         env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
   1078     }
   1079 
   1080     {
   1081         // TIFF/EP standard id
   1082         uint8_t standardId[] = { 1, 0, 0, 0 };
   1083         BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
   1084                 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
   1085     }
   1086 
   1087     {
   1088         // copyright
   1089         uint8_t copyright = '\0'; // empty
   1090         BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
   1091                 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
   1092     }
   1093 
   1094     {
   1095         // exposure time
   1096         camera_metadata_entry entry =
   1097             results.find(ANDROID_SENSOR_EXPOSURE_TIME);
   1098         BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
   1099 
   1100         int64_t exposureTime = *(entry.data.i64);
   1101 
   1102         if (exposureTime < 0) {
   1103             // Should be unreachable
   1104             jniThrowException(env, "java/lang/IllegalArgumentException",
   1105                     "Negative exposure time in metadata");
   1106             return;
   1107         }
   1108 
   1109         // Ensure exposure time doesn't overflow (for exposures > 4s)
   1110         uint32_t denominator = 1000000000;
   1111         while (exposureTime > UINT32_MAX) {
   1112             exposureTime >>= 1;
   1113             denominator >>= 1;
   1114             if (denominator == 0) {
   1115                 // Should be unreachable
   1116                 jniThrowException(env, "java/lang/IllegalArgumentException",
   1117                         "Exposure time too long");
   1118                 return;
   1119             }
   1120         }
   1121 
   1122         uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
   1123         BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
   1124                 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
   1125 
   1126     }
   1127 
   1128     {
   1129         // ISO speed ratings
   1130         camera_metadata_entry entry =
   1131             results.find(ANDROID_SENSOR_SENSITIVITY);
   1132         BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
   1133 
   1134         int32_t tempIso = *(entry.data.i32);
   1135         if (tempIso < 0) {
   1136             jniThrowException(env, "java/lang/IllegalArgumentException",
   1137                                     "Negative ISO value");
   1138             return;
   1139         }
   1140 
   1141         if (tempIso > UINT16_MAX) {
   1142             ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
   1143             tempIso = UINT16_MAX;
   1144         }
   1145 
   1146         uint16_t iso = static_cast<uint16_t>(tempIso);
   1147         BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
   1148                 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
   1149     }
   1150 
   1151     {
   1152         // focal length
   1153         camera_metadata_entry entry =
   1154             results.find(ANDROID_LENS_FOCAL_LENGTH);
   1155         BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
   1156 
   1157         uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
   1158         BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
   1159                 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
   1160     }
   1161 
   1162     {
   1163         // f number
   1164         camera_metadata_entry entry =
   1165             results.find(ANDROID_LENS_APERTURE);
   1166         BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
   1167 
   1168         uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
   1169         BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
   1170                 TIFF_IFD_0), env, TAG_FNUMBER, writer);
   1171     }
   1172 
   1173     {
   1174         // Set DNG version information
   1175         uint8_t version[4] = {1, 4, 0, 0};
   1176         BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
   1177                 env, TAG_DNGVERSION, writer);
   1178 
   1179         uint8_t backwardVersion[4] = {1, 1, 0, 0};
   1180         BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
   1181                 env, TAG_DNGBACKWARDVERSION, writer);
   1182     }
   1183 
   1184     {
   1185         // Set whitelevel
   1186         camera_metadata_entry entry =
   1187                 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
   1188         BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
   1189         uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
   1190         BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
   1191                 TAG_WHITELEVEL, writer);
   1192     }
   1193 
   1194     {
   1195         // Set default scale
   1196         uint32_t defaultScale[4] = {1, 1, 1, 1};
   1197         BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
   1198                 env, TAG_DEFAULTSCALE, writer);
   1199     }
   1200 
   1201     bool singleIlluminant = false;
   1202     {
   1203         // Set calibration illuminants
   1204         camera_metadata_entry entry1 =
   1205             characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
   1206         BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
   1207         camera_metadata_entry entry2 =
   1208             characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
   1209         if (entry2.count == 0) {
   1210             singleIlluminant = true;
   1211         }
   1212         uint16_t ref1 = entry1.data.u8[0];
   1213 
   1214         BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
   1215                 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
   1216 
   1217         if (!singleIlluminant) {
   1218             uint16_t ref2 = entry2.data.u8[0];
   1219             BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
   1220                     TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
   1221         }
   1222     }
   1223 
   1224     {
   1225         // Set color transforms
   1226         camera_metadata_entry entry1 =
   1227             characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
   1228         BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
   1229 
   1230         int32_t colorTransform1[entry1.count * 2];
   1231 
   1232         size_t ctr = 0;
   1233         for(size_t i = 0; i < entry1.count; ++i) {
   1234             colorTransform1[ctr++] = entry1.data.r[i].numerator;
   1235             colorTransform1[ctr++] = entry1.data.r[i].denominator;
   1236         }
   1237 
   1238         BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
   1239                 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
   1240 
   1241         if (!singleIlluminant) {
   1242             camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
   1243             BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
   1244             int32_t colorTransform2[entry2.count * 2];
   1245 
   1246             ctr = 0;
   1247             for(size_t i = 0; i < entry2.count; ++i) {
   1248                 colorTransform2[ctr++] = entry2.data.r[i].numerator;
   1249                 colorTransform2[ctr++] = entry2.data.r[i].denominator;
   1250             }
   1251 
   1252             BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
   1253                     TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
   1254         }
   1255     }
   1256 
   1257     {
   1258         // Set calibration transforms
   1259         camera_metadata_entry entry1 =
   1260             characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
   1261         BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
   1262 
   1263         int32_t calibrationTransform1[entry1.count * 2];
   1264 
   1265         size_t ctr = 0;
   1266         for(size_t i = 0; i < entry1.count; ++i) {
   1267             calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
   1268             calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
   1269         }
   1270 
   1271         BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
   1272                 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
   1273 
   1274         if (!singleIlluminant) {
   1275             camera_metadata_entry entry2 =
   1276                 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
   1277             BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
   1278             int32_t calibrationTransform2[entry2.count * 2];
   1279 
   1280             ctr = 0;
   1281             for(size_t i = 0; i < entry2.count; ++i) {
   1282                 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
   1283                 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
   1284             }
   1285 
   1286             BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
   1287                     calibrationTransform2, TIFF_IFD_0),  env, TAG_CAMERACALIBRATION2, writer);
   1288         }
   1289     }
   1290 
   1291     {
   1292         // Set forward transforms
   1293         camera_metadata_entry entry1 =
   1294             characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
   1295         BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
   1296 
   1297         int32_t forwardTransform1[entry1.count * 2];
   1298 
   1299         size_t ctr = 0;
   1300         for(size_t i = 0; i < entry1.count; ++i) {
   1301             forwardTransform1[ctr++] = entry1.data.r[i].numerator;
   1302             forwardTransform1[ctr++] = entry1.data.r[i].denominator;
   1303         }
   1304 
   1305         BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
   1306                 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
   1307 
   1308         if (!singleIlluminant) {
   1309             camera_metadata_entry entry2 =
   1310                 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
   1311             BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
   1312             int32_t forwardTransform2[entry2.count * 2];
   1313 
   1314             ctr = 0;
   1315             for(size_t i = 0; i < entry2.count; ++i) {
   1316                 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
   1317                 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
   1318             }
   1319 
   1320             BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
   1321                     TIFF_IFD_0),  env, TAG_FORWARDMATRIX2, writer);
   1322         }
   1323     }
   1324 
   1325     {
   1326         // Set camera neutral
   1327         camera_metadata_entry entry =
   1328             results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
   1329         BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
   1330         uint32_t cameraNeutral[entry.count * 2];
   1331 
   1332         size_t ctr = 0;
   1333         for(size_t i = 0; i < entry.count; ++i) {
   1334             cameraNeutral[ctr++] =
   1335                     static_cast<uint32_t>(entry.data.r[i].numerator);
   1336             cameraNeutral[ctr++] =
   1337                     static_cast<uint32_t>(entry.data.r[i].denominator);
   1338         }
   1339 
   1340         BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
   1341                 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
   1342     }
   1343 
   1344     {
   1345         // Setup data strips
   1346         // TODO: Switch to tiled implementation.
   1347         if (writer->addStrip(TIFF_IFD_0) != OK) {
   1348             ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
   1349             jniThrowException(env, "java/lang/IllegalStateException",
   1350                     "Failed to setup strip tags.");
   1351             return;
   1352         }
   1353     }
   1354 
   1355     {
   1356         // Setup default crop + crop origin tags
   1357         uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
   1358         uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
   1359         if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
   1360             uint32_t defaultCropOrigin[] = {margin, margin};
   1361             uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
   1362             BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
   1363                     TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
   1364             BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
   1365                     TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
   1366         }
   1367     }
   1368 
   1369     {
   1370         // Setup unique camera model tag
   1371         char model[PROPERTY_VALUE_MAX];
   1372         property_get("ro.product.model", model, "");
   1373 
   1374         char manufacturer[PROPERTY_VALUE_MAX];
   1375         property_get("ro.product.manufacturer", manufacturer, "");
   1376 
   1377         char brand[PROPERTY_VALUE_MAX];
   1378         property_get("ro.product.brand", brand, "");
   1379 
   1380         String8 cameraModel(model);
   1381         cameraModel += "-";
   1382         cameraModel += manufacturer;
   1383         cameraModel += "-";
   1384         cameraModel += brand;
   1385 
   1386         BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
   1387                 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
   1388                 TAG_UNIQUECAMERAMODEL, writer);
   1389     }
   1390 
   1391     {
   1392         // Setup sensor noise model
   1393         camera_metadata_entry entry =
   1394             results.find(ANDROID_SENSOR_NOISE_PROFILE);
   1395 
   1396         const status_t numPlaneColors = 3;
   1397         const status_t numCfaChannels = 4;
   1398 
   1399         uint8_t cfaOut[numCfaChannels];
   1400         if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
   1401             jniThrowException(env, "java/lang/IllegalArgumentException",
   1402                     "Invalid CFA from camera characteristics");
   1403             return;
   1404         }
   1405 
   1406         double noiseProfile[numPlaneColors * 2];
   1407 
   1408         if (entry.count > 0) {
   1409             if (entry.count != numCfaChannels * 2) {
   1410                 ALOGW("%s: Invalid entry count %u for noise profile returned in characteristics,"
   1411                         " no noise profile tag written...", __FUNCTION__, entry.count);
   1412             } else {
   1413                 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
   1414                         cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
   1415 
   1416                     BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
   1417                             noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
   1418                 } else {
   1419                     ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
   1420                             " tag written...", __FUNCTION__);
   1421                 }
   1422             }
   1423         } else {
   1424             ALOGW("%s: No noise profile found in result metadata.  Image quality may be reduced.",
   1425                     __FUNCTION__);
   1426         }
   1427     }
   1428 
   1429     {
   1430         // Setup opcode List 2
   1431         camera_metadata_entry entry1 =
   1432                 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
   1433 
   1434         uint32_t lsmWidth = 0;
   1435         uint32_t lsmHeight = 0;
   1436 
   1437         if (entry1.count != 0) {
   1438             lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
   1439             lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
   1440         }
   1441 
   1442         camera_metadata_entry entry2 =
   1443                 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
   1444 
   1445         if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
   1446 
   1447             OpcodeListBuilder builder;
   1448             status_t err = builder.addGainMapsForMetadata(lsmWidth,
   1449                                                           lsmHeight,
   1450                                                           0,
   1451                                                           0,
   1452                                                           imageHeight,
   1453                                                           imageWidth,
   1454                                                           opcodeCfaLayout,
   1455                                                           entry2.data.f);
   1456             if (err == OK) {
   1457                 size_t listSize = builder.getSize();
   1458                 uint8_t opcodeListBuf[listSize];
   1459                 err = builder.buildOpList(opcodeListBuf);
   1460                 if (err == OK) {
   1461                     BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
   1462                             TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
   1463                 } else {
   1464                     ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
   1465                     jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
   1466                 }
   1467             } else {
   1468                 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
   1469                 jniThrowRuntimeException(env, "failed to add lens shading map.");
   1470             }
   1471         } else {
   1472             ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
   1473                     __FUNCTION__);
   1474         }
   1475     }
   1476 
   1477     DngCreator_setNativeContext(env, thiz, nativeContext);
   1478 }
   1479 
   1480 static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
   1481     ALOGV("%s:", __FUNCTION__);
   1482     DngCreator_setNativeContext(env, thiz, NULL);
   1483 }
   1484 
   1485 static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
   1486     ALOGV("%s:", __FUNCTION__);
   1487 
   1488     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1489     if (writer == NULL) {
   1490         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1491         jniThrowException(env, "java/lang/AssertionError",
   1492                 "setOrientation called with uninitialized DngCreator");
   1493         return;
   1494     }
   1495 
   1496     uint16_t orientation = static_cast<uint16_t>(orient);
   1497     BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
   1498                 TAG_ORIENTATION, writer);
   1499 
   1500     // Set main image orientation also if in a separate IFD
   1501     if (writer->hasIfd(TIFF_IFD_SUB1)) {
   1502         BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
   1503                     TAG_ORIENTATION, writer);
   1504     }
   1505 }
   1506 
   1507 static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
   1508     ALOGV("%s:", __FUNCTION__);
   1509 
   1510     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1511     if (writer == NULL) {
   1512         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1513         jniThrowException(env, "java/lang/AssertionError",
   1514                 "setDescription called with uninitialized DngCreator");
   1515         return;
   1516     }
   1517 
   1518     const char* desc = env->GetStringUTFChars(description, NULL);
   1519     size_t len = strlen(desc) + 1;
   1520 
   1521     if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
   1522             reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
   1523         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
   1524                 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
   1525     }
   1526 
   1527     env->ReleaseStringUTFChars(description, desc);
   1528 }
   1529 
   1530 static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
   1531         jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
   1532     ALOGV("%s:", __FUNCTION__);
   1533 
   1534     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1535     if (writer == NULL) {
   1536         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1537         jniThrowException(env, "java/lang/AssertionError",
   1538                 "setGpsTags called with uninitialized DngCreator");
   1539         return;
   1540     }
   1541 
   1542     if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
   1543         if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
   1544             ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
   1545                     TIFF_IFD_0);
   1546             jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
   1547             return;
   1548         }
   1549     }
   1550 
   1551     const jsize GPS_VALUE_LENGTH = 6;
   1552     jsize latLen = env->GetArrayLength(latTag);
   1553     jsize longLen = env->GetArrayLength(longTag);
   1554     jsize timeLen = env->GetArrayLength(timeTag);
   1555     if (latLen != GPS_VALUE_LENGTH) {
   1556         jniThrowException(env, "java/lang/IllegalArgumentException",
   1557                 "invalid latitude tag length");
   1558         return;
   1559     } else if (longLen != GPS_VALUE_LENGTH) {
   1560         jniThrowException(env, "java/lang/IllegalArgumentException",
   1561                 "invalid longitude tag length");
   1562         return;
   1563     } else if (timeLen != GPS_VALUE_LENGTH) {
   1564         jniThrowException(env, "java/lang/IllegalArgumentException",
   1565                 "invalid time tag length");
   1566         return;
   1567     }
   1568 
   1569     uint32_t latitude[GPS_VALUE_LENGTH];
   1570     uint32_t longitude[GPS_VALUE_LENGTH];
   1571     uint32_t timestamp[GPS_VALUE_LENGTH];
   1572 
   1573     env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
   1574             reinterpret_cast<jint*>(&latitude));
   1575     env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
   1576             reinterpret_cast<jint*>(&longitude));
   1577     env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
   1578             reinterpret_cast<jint*>(&timestamp));
   1579 
   1580     const jsize GPS_REF_LENGTH = 2;
   1581     const jsize GPS_DATE_LENGTH = 11;
   1582     uint8_t latitudeRef[GPS_REF_LENGTH];
   1583     uint8_t longitudeRef[GPS_REF_LENGTH];
   1584     uint8_t date[GPS_DATE_LENGTH];
   1585 
   1586     env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
   1587     latitudeRef[GPS_REF_LENGTH - 1] = '\0';
   1588     env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
   1589     longitudeRef[GPS_REF_LENGTH - 1] = '\0';
   1590 
   1591     env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
   1592     date[GPS_DATE_LENGTH - 1] = '\0';
   1593 
   1594     {
   1595         uint8_t version[] = {2, 3, 0, 0};
   1596         BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
   1597                 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
   1598     }
   1599 
   1600     {
   1601         BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
   1602                 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
   1603     }
   1604 
   1605     {
   1606         BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
   1607                 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
   1608     }
   1609 
   1610     {
   1611         BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
   1612                 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
   1613     }
   1614 
   1615     {
   1616         BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
   1617                 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
   1618     }
   1619 
   1620     {
   1621         BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
   1622                 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
   1623     }
   1624 
   1625     {
   1626         BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
   1627                 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
   1628     }
   1629 }
   1630 
   1631 static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
   1632         jint height) {
   1633     ALOGV("%s:", __FUNCTION__);
   1634 
   1635     NativeContext* context = DngCreator_getNativeContext(env, thiz);
   1636     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1637     if (writer == NULL || context == NULL) {
   1638         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1639         jniThrowException(env, "java/lang/AssertionError",
   1640                 "setThumbnail called with uninitialized DngCreator");
   1641         return;
   1642     }
   1643 
   1644     size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
   1645     jlong capacity = env->GetDirectBufferCapacity(buffer);
   1646     if (capacity != fullSize) {
   1647         jniThrowExceptionFmt(env, "java/lang/AssertionError",
   1648                 "Invalid size %d for thumbnail, expected size was %d",
   1649                 capacity, fullSize);
   1650         return;
   1651     }
   1652 
   1653     uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
   1654     if (pixelBytes == NULL) {
   1655         ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
   1656         jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
   1657         return;
   1658     }
   1659 
   1660     if (!writer->hasIfd(TIFF_IFD_SUB1)) {
   1661         if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
   1662             ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
   1663                     TIFF_IFD_0);
   1664             jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
   1665             return;
   1666         }
   1667 
   1668         Vector<uint16_t> tagsToMove;
   1669         tagsToMove.add(TAG_ORIENTATION);
   1670         tagsToMove.add(TAG_NEWSUBFILETYPE);
   1671         tagsToMove.add(TAG_BITSPERSAMPLE);
   1672         tagsToMove.add(TAG_COMPRESSION);
   1673         tagsToMove.add(TAG_IMAGEWIDTH);
   1674         tagsToMove.add(TAG_IMAGELENGTH);
   1675         tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
   1676         tagsToMove.add(TAG_BLACKLEVEL);
   1677         tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
   1678         tagsToMove.add(TAG_SAMPLESPERPIXEL);
   1679         tagsToMove.add(TAG_PLANARCONFIGURATION);
   1680         tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
   1681         tagsToMove.add(TAG_CFAPATTERN);
   1682         tagsToMove.add(TAG_CFAPLANECOLOR);
   1683         tagsToMove.add(TAG_CFALAYOUT);
   1684         tagsToMove.add(TAG_XRESOLUTION);
   1685         tagsToMove.add(TAG_YRESOLUTION);
   1686         tagsToMove.add(TAG_RESOLUTIONUNIT);
   1687         tagsToMove.add(TAG_WHITELEVEL);
   1688         tagsToMove.add(TAG_DEFAULTSCALE);
   1689         tagsToMove.add(TAG_ROWSPERSTRIP);
   1690         tagsToMove.add(TAG_STRIPBYTECOUNTS);
   1691         tagsToMove.add(TAG_STRIPOFFSETS);
   1692         tagsToMove.add(TAG_DEFAULTCROPORIGIN);
   1693         tagsToMove.add(TAG_DEFAULTCROPSIZE);
   1694         tagsToMove.add(TAG_OPCODELIST2);
   1695 
   1696         if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
   1697             jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
   1698             return;
   1699         }
   1700 
   1701         // Make sure both IFDs get the same orientation tag
   1702         sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
   1703         if (orientEntry != NULL) {
   1704             writer->addEntry(orientEntry, TIFF_IFD_0);
   1705         }
   1706     }
   1707 
   1708     // Setup thumbnail tags
   1709 
   1710     {
   1711         // Set photometric interpretation
   1712         uint16_t interpretation = 2; // RGB
   1713         BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
   1714                 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
   1715     }
   1716 
   1717     {
   1718         // Set planar configuration
   1719         uint16_t config = 1; // Chunky
   1720         BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
   1721                 env, TAG_PLANARCONFIGURATION, writer);
   1722     }
   1723 
   1724     {
   1725         // Set samples per pixel
   1726         uint16_t samples = SAMPLES_PER_RGB_PIXEL;
   1727         BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
   1728                 env, TAG_SAMPLESPERPIXEL, writer);
   1729     }
   1730 
   1731     {
   1732         // Set bits per sample
   1733         uint16_t bits = BITS_PER_RGB_SAMPLE;
   1734         BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
   1735                 TAG_BITSPERSAMPLE, writer);
   1736     }
   1737 
   1738     {
   1739         // Set subfiletype
   1740         uint32_t subfileType = 1; // Thumbnail image
   1741         BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
   1742                 TAG_NEWSUBFILETYPE, writer);
   1743     }
   1744 
   1745     {
   1746         // Set compression
   1747         uint16_t compression = 1; // None
   1748         BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
   1749                 TAG_COMPRESSION, writer);
   1750     }
   1751 
   1752     {
   1753         // Set dimensions
   1754         uint32_t uWidth = static_cast<uint32_t>(width);
   1755         uint32_t uHeight = static_cast<uint32_t>(height);
   1756         BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
   1757                 TAG_IMAGEWIDTH, writer);
   1758         BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
   1759                 TAG_IMAGELENGTH, writer);
   1760     }
   1761 
   1762     {
   1763         // x resolution
   1764         uint32_t xres[] = { 72, 1 }; // default 72 ppi
   1765         BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
   1766                 env, TAG_XRESOLUTION, writer);
   1767 
   1768         // y resolution
   1769         uint32_t yres[] = { 72, 1 }; // default 72 ppi
   1770         BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
   1771                 env, TAG_YRESOLUTION, writer);
   1772 
   1773         uint16_t unit = 2; // inches
   1774         BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
   1775                 env, TAG_RESOLUTIONUNIT, writer);
   1776     }
   1777 
   1778     {
   1779         // Setup data strips
   1780         if (writer->addStrip(TIFF_IFD_0) != OK) {
   1781             ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
   1782             jniThrowException(env, "java/lang/IllegalStateException",
   1783                     "Failed to setup thumbnail strip tags.");
   1784             return;
   1785         }
   1786         if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
   1787             ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
   1788             jniThrowException(env, "java/lang/IllegalStateException",
   1789                     "Failed to setup main image strip tags.");
   1790             return;
   1791         }
   1792     }
   1793 
   1794     if (!context->setThumbnail(pixelBytes, width, height)) {
   1795         jniThrowException(env, "java/lang/IllegalStateException",
   1796                 "Failed to set thumbnail.");
   1797         return;
   1798     }
   1799 }
   1800 
   1801 // TODO: Refactor out common preamble for the two nativeWrite methods.
   1802 static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
   1803         jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
   1804         jboolean isDirect) {
   1805     ALOGV("%s:", __FUNCTION__);
   1806     ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d,"
   1807               " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset);
   1808     uint32_t rStride = static_cast<uint32_t>(rowStride);
   1809     uint32_t pStride = static_cast<uint32_t>(pixStride);
   1810     uint32_t uWidth = static_cast<uint32_t>(width);
   1811     uint32_t uHeight = static_cast<uint32_t>(height);
   1812     uint64_t uOffset = static_cast<uint64_t>(offset);
   1813 
   1814     sp<JniOutputStream> out = new JniOutputStream(env, outStream);
   1815     if(env->ExceptionCheck()) {
   1816         ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
   1817         return;
   1818     }
   1819 
   1820     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1821     NativeContext* context = DngCreator_getNativeContext(env, thiz);
   1822     if (writer == NULL || context == NULL) {
   1823         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1824         jniThrowException(env, "java/lang/AssertionError",
   1825                 "Write called with uninitialized DngCreator");
   1826         return;
   1827     }
   1828 
   1829     // Validate DNG header
   1830     if (!validateDngHeader(env, writer, width, height)) {
   1831         return;
   1832     }
   1833 
   1834     sp<JniInputByteBuffer> inBuf;
   1835     Vector<StripSource*> sources;
   1836     sp<DirectStripSource> thumbnailSource;
   1837     uint32_t targetIfd = TIFF_IFD_0;
   1838 
   1839     bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
   1840 
   1841     if (hasThumbnail) {
   1842         ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
   1843         uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
   1844         uint32_t thumbWidth = context->getThumbnailWidth();
   1845         thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
   1846                 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
   1847                 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
   1848                 SAMPLES_PER_RGB_PIXEL);
   1849         sources.add(thumbnailSource.get());
   1850         targetIfd = TIFF_IFD_SUB1;
   1851     }
   1852 
   1853     if (isDirect) {
   1854         size_t fullSize = rStride * uHeight;
   1855         jlong capacity = env->GetDirectBufferCapacity(inBuffer);
   1856         if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
   1857             jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
   1858                     "Invalid size %d for Image, size given in metadata is %d at current stride",
   1859                     capacity, fullSize);
   1860             return;
   1861         }
   1862 
   1863         uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
   1864         if (pixelBytes == NULL) {
   1865             ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
   1866             jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
   1867             return;
   1868         }
   1869 
   1870         ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
   1871         DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
   1872                 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
   1873         sources.add(&stripSource);
   1874 
   1875         status_t ret = OK;
   1876         if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
   1877             ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
   1878             if (!env->ExceptionCheck()) {
   1879                 jniThrowExceptionFmt(env, "java/io/IOException",
   1880                         "Encountered error %d while writing file.", ret);
   1881             }
   1882             return;
   1883         }
   1884     } else {
   1885         inBuf = new JniInputByteBuffer(env, inBuffer);
   1886 
   1887         ALOGV("%s: Using input-type strip source.", __FUNCTION__);
   1888         InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
   1889                  rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
   1890         sources.add(&stripSource);
   1891 
   1892         status_t ret = OK;
   1893         if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
   1894             ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
   1895             if (!env->ExceptionCheck()) {
   1896                 jniThrowExceptionFmt(env, "java/io/IOException",
   1897                         "Encountered error %d while writing file.", ret);
   1898             }
   1899             return;
   1900         }
   1901     }
   1902 }
   1903 
   1904 static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
   1905         jobject inStream, jint width, jint height, jlong offset) {
   1906     ALOGV("%s:", __FUNCTION__);
   1907 
   1908     uint32_t rowStride = width * BYTES_PER_SAMPLE;
   1909     uint32_t pixStride = BYTES_PER_SAMPLE;
   1910     uint32_t uWidth = static_cast<uint32_t>(width);
   1911     uint32_t uHeight = static_cast<uint32_t>(height);
   1912     uint64_t uOffset = static_cast<uint32_t>(offset);
   1913 
   1914     ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u,"
   1915               "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride,
   1916               offset);
   1917 
   1918     sp<JniOutputStream> out = new JniOutputStream(env, outStream);
   1919     if(env->ExceptionCheck()) {
   1920         ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
   1921         return;
   1922     }
   1923 
   1924     TiffWriter* writer = DngCreator_getCreator(env, thiz);
   1925     NativeContext* context = DngCreator_getNativeContext(env, thiz);
   1926     if (writer == NULL || context == NULL) {
   1927         ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
   1928         jniThrowException(env, "java/lang/AssertionError",
   1929                 "Write called with uninitialized DngCreator");
   1930         return;
   1931     }
   1932 
   1933     // Validate DNG header
   1934     if (!validateDngHeader(env, writer, width, height)) {
   1935         return;
   1936     }
   1937 
   1938     sp<DirectStripSource> thumbnailSource;
   1939     uint32_t targetIfd = TIFF_IFD_0;
   1940     bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
   1941     Vector<StripSource*> sources;
   1942 
   1943     if (hasThumbnail) {
   1944         ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
   1945         uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
   1946         uint32_t width = context->getThumbnailWidth();
   1947         thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
   1948                 width, context->getThumbnailHeight(), bytesPerPixel,
   1949                 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
   1950                 SAMPLES_PER_RGB_PIXEL);
   1951         sources.add(thumbnailSource.get());
   1952         targetIfd = TIFF_IFD_SUB1;
   1953     }
   1954 
   1955     sp<JniInputStream> in = new JniInputStream(env, inStream);
   1956 
   1957     ALOGV("%s: Using input-type strip source.", __FUNCTION__);
   1958     InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
   1959              rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
   1960     sources.add(&stripSource);
   1961 
   1962     status_t ret = OK;
   1963     if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
   1964         ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
   1965         if (!env->ExceptionCheck()) {
   1966             jniThrowExceptionFmt(env, "java/io/IOException",
   1967                     "Encountered error %d while writing file.", ret);
   1968         }
   1969         return;
   1970     }
   1971 }
   1972 
   1973 } /*extern "C" */
   1974 
   1975 static JNINativeMethod gDngCreatorMethods[] = {
   1976     {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
   1977     {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
   1978             "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
   1979             (void*) DngCreator_init},
   1980     {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
   1981     {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
   1982     {"nativeSetDescription",    "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
   1983     {"nativeSetGpsTags",    "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
   1984             (void*) DngCreator_nativeSetGpsTags},
   1985     {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
   1986     {"nativeWriteImage",        "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
   1987             (void*) DngCreator_nativeWriteImage},
   1988     {"nativeWriteInputStream",    "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
   1989             (void*) DngCreator_nativeWriteInputStream},
   1990 };
   1991 
   1992 int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
   1993     return AndroidRuntime::registerNativeMethods(env,
   1994                    "android/hardware/camera2/DngCreator", gDngCreatorMethods,
   1995                    NELEM(gDngCreatorMethods));
   1996 }
   1997