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