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