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