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