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