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