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