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