android_hardware_camera2_DngCreator.cpp revision 3e1902504979b9b456a14dffa6507ee2d9ea3d6a
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
20#include <system/camera_metadata.h>
21#include <camera/CameraMetadata.h>
22#include <img_utils/DngUtils.h>
23#include <img_utils/TagDefinitions.h>
24#include <img_utils/TiffIfd.h>
25#include <img_utils/TiffWriter.h>
26#include <img_utils/Output.h>
27#include <img_utils/Input.h>
28#include <img_utils/StripSource.h>
29
30#include <utils/Log.h>
31#include <utils/Errors.h>
32#include <utils/StrongPointer.h>
33#include <utils/RefBase.h>
34#include <utils/Vector.h>
35#include <cutils/properties.h>
36
37#include <string.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    status_t err = OK;
431    uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
432    jlong offset = mOffset;
433
434    if (fullSize != count) {
435        ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
436                fullSize);
437        jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
438        return BAD_VALUE;
439    }
440
441    // Skip offset
442    while (offset > 0) {
443        ssize_t skipped = mInput->skip(offset);
444        if (skipped <= 0) {
445            if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
446                jniThrowExceptionFmt(mEnv, "java/io/IOException",
447                        "Early EOF encountered in skip, not enough pixel data for image of size %u",
448                        fullSize);
449                skipped = NOT_ENOUGH_DATA;
450            } else {
451                if (!mEnv->ExceptionCheck()) {
452                    jniThrowException(mEnv, "java/io/IOException",
453                            "Error encountered while skip bytes in input stream.");
454                }
455            }
456
457            return skipped;
458        }
459        offset -= skipped;
460    }
461
462    Vector<uint8_t> row;
463    if (row.resize(mRowStride) < 0) {
464        jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
465        return BAD_VALUE;
466    }
467
468    uint8_t* rowBytes = row.editArray();
469
470    for (uint32_t i = 0; i < mHeight; ++i) {
471        size_t rowFillAmt = 0;
472        size_t rowSize = mPixStride;
473
474        while (rowFillAmt < mRowStride) {
475            ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
476            if (bytesRead <= 0) {
477                if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
478                    jniThrowExceptionFmt(mEnv, "java/io/IOException",
479                            "Early EOF encountered, not enough pixel data for image of size %u",
480                            fullSize);
481                    bytesRead = NOT_ENOUGH_DATA;
482                } else {
483                    if (!mEnv->ExceptionCheck()) {
484                        jniThrowException(mEnv, "java/io/IOException",
485                                "Error encountered while reading");
486                    }
487                }
488                return bytesRead;
489            }
490            rowFillAmt += bytesRead;
491            rowSize -= bytesRead;
492        }
493
494        if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
495            ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
496
497            if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
498                    mEnv->ExceptionCheck()) {
499                if (!mEnv->ExceptionCheck()) {
500                    jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
501                }
502                return BAD_VALUE;
503            }
504        } else {
505            ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
506            jniThrowException(mEnv, "java/lang/IllegalStateException",
507                    "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
508            return BAD_VALUE;
509
510            // TODO: Add support for non-contiguous pixels if needed.
511        }
512    }
513    return OK;
514}
515
516uint32_t InputStripSource::getIfd() const {
517    return mIfd;
518}
519
520// End of InputStripSource
521// ----------------------------------------------------------------------------
522
523/**
524 * StripSource subclass for direct buffer types.
525 *
526 * This class is not intended to be used across JNI calls.
527 */
528
529class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
530public:
531    DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
532            uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
533            uint32_t bytesPerSample, uint32_t samplesPerPixel);
534
535    virtual ~DirectStripSource();
536
537    virtual status_t writeToStream(Output& stream, uint32_t count);
538
539    virtual uint32_t getIfd() const;
540protected:
541    uint32_t mIfd;
542    const uint8_t* mPixelBytes;
543    uint32_t mWidth;
544    uint32_t mHeight;
545    uint32_t mPixStride;
546    uint32_t mRowStride;
547    uint16_t mOffset;
548    JNIEnv* mEnv;
549    uint32_t mBytesPerSample;
550    uint32_t mSamplesPerPixel;
551};
552
553DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
554            uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
555            uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
556            mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
557            mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
558            mSamplesPerPixel(samplesPerPixel) {}
559
560DirectStripSource::~DirectStripSource() {}
561
562status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
563    uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
564
565    if (fullSize != count) {
566        ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
567                fullSize);
568        jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
569        return BAD_VALUE;
570    }
571
572    if (mPixStride == mBytesPerSample * mSamplesPerPixel
573            && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
574        ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
575
576        if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
577            if (!mEnv->ExceptionCheck()) {
578                jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
579            }
580            return BAD_VALUE;
581        }
582    } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
583        ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
584
585        for (size_t i = 0; i < mHeight; ++i) {
586            if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
587                        mEnv->ExceptionCheck()) {
588                if (!mEnv->ExceptionCheck()) {
589                    jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
590                }
591                return BAD_VALUE;
592            }
593        }
594    } else {
595        ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
596
597        jniThrowException(mEnv, "java/lang/IllegalStateException",
598                "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
599        return BAD_VALUE;
600
601        // TODO: Add support for non-contiguous pixels if needed.
602    }
603    return OK;
604
605}
606
607uint32_t DirectStripSource::getIfd() const {
608    return mIfd;
609}
610
611// End of DirectStripSource
612// ----------------------------------------------------------------------------
613
614static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
615    bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
616
617    // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
618    uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
619    uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
620
621    if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
622        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
623                        "Metadata width %d doesn't match image width %d", metadataWidth, width);
624        return false;
625    }
626
627    if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
628        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
629                        "Metadata height %d doesn't match image height %d", metadataHeight, height);
630        return false;
631    }
632
633    return true;
634}
635
636static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
637        const Vector<uint16_t>& entries) {
638    for (size_t i = 0; i < entries.size(); ++i) {
639        uint16_t tagId = entries[i];
640        sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
641        if (entry == NULL) {
642            ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
643                    ifdFrom);
644            return BAD_VALUE;
645        }
646        if (writer->addEntry(entry, ifdTo) != OK) {
647            ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
648                    ifdFrom);
649            return BAD_VALUE;
650        }
651        writer->removeEntry(tagId, ifdFrom);
652    }
653    return OK;
654}
655
656// ----------------------------------------------------------------------------
657extern "C" {
658
659static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
660    ALOGV("%s:", __FUNCTION__);
661    return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
662            gDngCreatorClassInfo.mNativeContext));
663}
664
665static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
666    ALOGV("%s:", __FUNCTION__);
667    NativeContext* current = DngCreator_getNativeContext(env, thiz);
668
669    if (context != NULL) {
670        context->incStrong((void*) DngCreator_setNativeContext);
671    }
672
673    if (current) {
674        current->decStrong((void*) DngCreator_setNativeContext);
675    }
676
677    env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
678            reinterpret_cast<jlong>(context.get()));
679}
680
681static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
682    ALOGV("%s:", __FUNCTION__);
683    NativeContext* current = DngCreator_getNativeContext(env, thiz);
684    if (current) {
685        return current->getWriter();
686    }
687    return NULL;
688}
689
690static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
691    ALOGV("%s:", __FUNCTION__);
692
693    gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
694            ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
695    LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
696            "can't find android/hardware/camera2/DngCreator.%s",
697            ANDROID_DNGCREATOR_CTX_JNI_ID);
698
699    jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
700    LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
701    gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
702    LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
703
704    jclass inputStreamClazz = env->FindClass("java/io/InputStream");
705    LOG_ALWAYS_FATAL_IF(inputStreamClazz == NULL, "Can't find java/io/InputStream class");
706    gInputStreamClassInfo.mReadMethod = env->GetMethodID(inputStreamClazz, "read", "([BII)I");
707    LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mReadMethod == NULL, "Can't find read method");
708    gInputStreamClassInfo.mSkipMethod = env->GetMethodID(inputStreamClazz, "skip", "(J)J");
709    LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mSkipMethod == NULL, "Can't find skip method");
710
711    jclass inputBufferClazz = env->FindClass("java/nio/ByteBuffer");
712    LOG_ALWAYS_FATAL_IF(inputBufferClazz == NULL, "Can't find java/nio/ByteBuffer class");
713    gInputByteBufferClassInfo.mGetMethod = env->GetMethodID(inputBufferClazz, "get",
714            "([BII)Ljava/nio/ByteBuffer;");
715    LOG_ALWAYS_FATAL_IF(gInputByteBufferClassInfo.mGetMethod == NULL, "Can't find get method");
716}
717
718static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
719        jobject resultsPtr, jstring formattedCaptureTime) {
720    ALOGV("%s:", __FUNCTION__);
721    CameraMetadata characteristics;
722    CameraMetadata results;
723    if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
724         jniThrowException(env, "java/lang/AssertionError",
725                "No native metadata defined for camera characteristics.");
726         return;
727    }
728    if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
729        jniThrowException(env, "java/lang/AssertionError",
730                "No native metadata defined for capture results.");
731        return;
732    }
733
734    sp<NativeContext> nativeContext = new NativeContext();
735    TiffWriter* writer = nativeContext->getWriter();
736
737    writer->addIfd(TIFF_IFD_0);
738
739    status_t err = OK;
740
741    const uint32_t samplesPerPixel = 1;
742    const uint32_t bitsPerSample = BITS_PER_SAMPLE;
743    const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
744    uint32_t imageWidth = 0;
745    uint32_t imageHeight = 0;
746
747    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
748
749    // TODO: Greensplit.
750    // TODO: Add remaining non-essential tags
751
752    // Setup main image tags
753
754    {
755        // Set orientation
756        uint16_t orientation = 1; // Normal
757        BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
758                TAG_ORIENTATION, writer);
759    }
760
761    {
762        // Set subfiletype
763        uint32_t subfileType = 0; // Main image
764        BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
765                TAG_NEWSUBFILETYPE, writer);
766    }
767
768    {
769        // Set bits per sample
770        uint16_t bits = static_cast<uint16_t>(bitsPerSample);
771        BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
772                TAG_BITSPERSAMPLE, writer);
773    }
774
775    {
776        // Set compression
777        uint16_t compression = 1; // None
778        BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
779                TAG_COMPRESSION, writer);
780    }
781
782    {
783        // Set dimensions
784        camera_metadata_entry entry =
785                characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
786        BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
787        uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
788        uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
789        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
790                TAG_IMAGEWIDTH, writer);
791        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
792                TAG_IMAGELENGTH, writer);
793        imageWidth = width;
794        imageHeight = height;
795    }
796
797    {
798        // Set photometric interpretation
799        uint16_t interpretation = 32803; // CFA
800        BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
801                TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
802    }
803
804    {
805        // Set blacklevel tags
806        camera_metadata_entry entry =
807                characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
808        BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
809        const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
810        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
811                TAG_BLACKLEVEL, writer);
812
813        uint16_t repeatDim[2] = {2, 2};
814        BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
815                TAG_BLACKLEVELREPEATDIM, writer);
816    }
817
818    {
819        // Set samples per pixel
820        uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
821        BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
822                env, TAG_SAMPLESPERPIXEL, writer);
823    }
824
825    {
826        // Set planar configuration
827        uint16_t config = 1; // Chunky
828        BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
829                env, TAG_PLANARCONFIGURATION, writer);
830    }
831
832    {
833        // Set CFA pattern dimensions
834        uint16_t repeatDim[2] = {2, 2};
835        BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
836                env, TAG_CFAREPEATPATTERNDIM, writer);
837    }
838
839    {
840        // Set CFA pattern
841        camera_metadata_entry entry =
842                        characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
843        BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
844        camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
845                static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
846                entry.data.u8[0]);
847        switch(cfa) {
848            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
849                uint8_t cfa[4] = {0, 1, 1, 2};
850                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
851                                                env, TAG_CFAPATTERN, writer);
852                opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
853                break;
854            }
855            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
856                uint8_t cfa[4] = {1, 0, 2, 1};
857                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
858                                                env, TAG_CFAPATTERN, writer);
859                opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
860                break;
861            }
862            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
863                uint8_t cfa[4] = {1, 2, 0, 1};
864                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
865                                                env, TAG_CFAPATTERN, writer);
866                opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
867                break;
868            }
869            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
870                uint8_t cfa[4] = {2, 1, 1, 0};
871                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
872                                env, TAG_CFAPATTERN, writer);
873                opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
874                break;
875            }
876            default: {
877                jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
878                            "Invalid metadata for tag %d", TAG_CFAPATTERN);
879                return;
880            }
881        }
882    }
883
884    {
885        // Set CFA plane color
886        uint8_t cfaPlaneColor[3] = {0, 1, 2};
887        BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
888                env, TAG_CFAPLANECOLOR, writer);
889    }
890
891    {
892        // Set CFA layout
893        uint16_t cfaLayout = 1;
894        BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
895                env, TAG_CFALAYOUT, writer);
896    }
897
898    {
899        // image description
900        uint8_t imageDescription = '\0'; // empty
901        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
902                env, TAG_IMAGEDESCRIPTION, writer);
903    }
904
905    {
906        // make
907        char manufacturer[PROPERTY_VALUE_MAX];
908
909        // Use "" to represent unknown make as suggested in TIFF/EP spec.
910        property_get("ro.product.manufacturer", manufacturer, "");
911        uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
912
913        BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
914                TIFF_IFD_0), env, TAG_MAKE, writer);
915    }
916
917    {
918        // model
919        char model[PROPERTY_VALUE_MAX];
920
921        // Use "" to represent unknown model as suggested in TIFF/EP spec.
922        property_get("ro.product.model", model, "");
923        uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
924
925        BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
926                TIFF_IFD_0), env, TAG_MODEL, writer);
927    }
928
929    {
930        // x resolution
931        uint32_t xres[] = { 72, 1 }; // default 72 ppi
932        BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
933                env, TAG_XRESOLUTION, writer);
934
935        // y resolution
936        uint32_t yres[] = { 72, 1 }; // default 72 ppi
937        BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
938                env, TAG_YRESOLUTION, writer);
939
940        uint16_t unit = 2; // inches
941        BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
942                env, TAG_RESOLUTIONUNIT, writer);
943    }
944
945    {
946        // software
947        char software[PROPERTY_VALUE_MAX];
948        property_get("ro.build.fingerprint", software, "");
949        uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
950        BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
951                TIFF_IFD_0), env, TAG_SOFTWARE, writer);
952    }
953
954    {
955        // datetime
956        const size_t DATETIME_COUNT = 20;
957        const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
958
959        size_t len = strlen(captureTime) + 1;
960        if (len != DATETIME_COUNT) {
961            jniThrowException(env, "java/lang/IllegalArgumentException",
962                    "Timestamp string length is not required 20 characters");
963            return;
964        }
965
966        if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
967                reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
968            env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
969            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
970                    "Invalid metadata for tag %x", TAG_DATETIME);
971            return;
972        }
973
974        // datetime original
975        if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
976                reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
977            env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
978            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
979                    "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
980            return;
981        }
982        env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
983    }
984
985    {
986        // TIFF/EP standard id
987        uint8_t standardId[] = { 1, 0, 0, 0 };
988        BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
989                TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
990    }
991
992    {
993        // copyright
994        uint8_t copyright = '\0'; // empty
995        BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
996                TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
997    }
998
999    {
1000        // exposure time
1001        camera_metadata_entry entry =
1002            results.find(ANDROID_SENSOR_EXPOSURE_TIME);
1003        BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
1004
1005        int64_t exposureTime = *(entry.data.i64);
1006
1007        if (exposureTime < 0) {
1008            // Should be unreachable
1009            jniThrowException(env, "java/lang/IllegalArgumentException",
1010                    "Negative exposure time in metadata");
1011            return;
1012        }
1013
1014        // Ensure exposure time doesn't overflow (for exposures > 4s)
1015        uint32_t denominator = 1000000000;
1016        while (exposureTime > UINT32_MAX) {
1017            exposureTime >>= 1;
1018            denominator >>= 1;
1019            if (denominator == 0) {
1020                // Should be unreachable
1021                jniThrowException(env, "java/lang/IllegalArgumentException",
1022                        "Exposure time too long");
1023                return;
1024            }
1025        }
1026
1027        uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
1028        BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
1029                TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
1030
1031    }
1032
1033    {
1034        // ISO speed ratings
1035        camera_metadata_entry entry =
1036            results.find(ANDROID_SENSOR_SENSITIVITY);
1037        BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
1038
1039        int32_t tempIso = *(entry.data.i32);
1040        if (tempIso < 0) {
1041            jniThrowException(env, "java/lang/IllegalArgumentException",
1042                                    "Negative ISO value");
1043            return;
1044        }
1045
1046        if (tempIso > UINT16_MAX) {
1047            ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1048            tempIso = UINT16_MAX;
1049        }
1050
1051        uint16_t iso = static_cast<uint16_t>(tempIso);
1052        BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
1053                TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
1054    }
1055
1056    {
1057        // focal length
1058        camera_metadata_entry entry =
1059            results.find(ANDROID_LENS_FOCAL_LENGTH);
1060        BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
1061
1062        uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1063        BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
1064                TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
1065    }
1066
1067    {
1068        // f number
1069        camera_metadata_entry entry =
1070            results.find(ANDROID_LENS_APERTURE);
1071        BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
1072
1073        uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1074        BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
1075                TIFF_IFD_0), env, TAG_FNUMBER, writer);
1076    }
1077
1078    {
1079        // Set DNG version information
1080        uint8_t version[4] = {1, 4, 0, 0};
1081        BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
1082                env, TAG_DNGVERSION, writer);
1083
1084        uint8_t backwardVersion[4] = {1, 1, 0, 0};
1085        BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
1086                env, TAG_DNGBACKWARDVERSION, writer);
1087    }
1088
1089    {
1090        // Set whitelevel
1091        camera_metadata_entry entry =
1092                characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
1093        BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
1094        uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
1095        BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
1096                TAG_WHITELEVEL, writer);
1097    }
1098
1099    {
1100        // Set default scale
1101        uint32_t defaultScale[4] = {1, 1, 1, 1};
1102        BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
1103                env, TAG_DEFAULTSCALE, writer);
1104    }
1105
1106    bool singleIlluminant = false;
1107    {
1108        // Set calibration illuminants
1109        camera_metadata_entry entry1 =
1110            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
1111        BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
1112        camera_metadata_entry entry2 =
1113            characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1114        if (entry2.count == 0) {
1115            singleIlluminant = true;
1116        }
1117        uint16_t ref1 = entry1.data.u8[0];
1118
1119        BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
1120                TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
1121
1122        if (!singleIlluminant) {
1123            uint16_t ref2 = entry2.data.u8[0];
1124            BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
1125                    TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
1126        }
1127    }
1128
1129    {
1130        // Set color transforms
1131        camera_metadata_entry entry1 =
1132            characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
1133        BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
1134
1135        int32_t colorTransform1[entry1.count * 2];
1136
1137        size_t ctr = 0;
1138        for(size_t i = 0; i < entry1.count; ++i) {
1139            colorTransform1[ctr++] = entry1.data.r[i].numerator;
1140            colorTransform1[ctr++] = entry1.data.r[i].denominator;
1141        }
1142
1143        BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1144                TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
1145
1146        if (!singleIlluminant) {
1147            camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
1148            BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
1149            int32_t colorTransform2[entry2.count * 2];
1150
1151            ctr = 0;
1152            for(size_t i = 0; i < entry2.count; ++i) {
1153                colorTransform2[ctr++] = entry2.data.r[i].numerator;
1154                colorTransform2[ctr++] = entry2.data.r[i].denominator;
1155            }
1156
1157            BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1158                    TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
1159        }
1160    }
1161
1162    {
1163        // Set calibration transforms
1164        camera_metadata_entry entry1 =
1165            characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
1166        BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
1167
1168        int32_t calibrationTransform1[entry1.count * 2];
1169
1170        size_t ctr = 0;
1171        for(size_t i = 0; i < entry1.count; ++i) {
1172            calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1173            calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1174        }
1175
1176        BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1177                calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
1178
1179        if (!singleIlluminant) {
1180            camera_metadata_entry entry2 =
1181                characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
1182            BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
1183            int32_t calibrationTransform2[entry2.count * 2];
1184
1185            ctr = 0;
1186            for(size_t i = 0; i < entry2.count; ++i) {
1187                calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1188                calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1189            }
1190
1191            BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
1192                    calibrationTransform1, TIFF_IFD_0),  env, TAG_CAMERACALIBRATION2, writer);
1193        }
1194    }
1195
1196    {
1197        // Set forward transforms
1198        camera_metadata_entry entry1 =
1199            characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
1200        BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
1201
1202        int32_t forwardTransform1[entry1.count * 2];
1203
1204        size_t ctr = 0;
1205        for(size_t i = 0; i < entry1.count; ++i) {
1206            forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1207            forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1208        }
1209
1210        BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
1211                TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
1212
1213        if (!singleIlluminant) {
1214            camera_metadata_entry entry2 =
1215                characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
1216            BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
1217            int32_t forwardTransform2[entry2.count * 2];
1218
1219            ctr = 0;
1220            for(size_t i = 0; i < entry2.count; ++i) {
1221                forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1222                forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1223            }
1224
1225            BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
1226                    TIFF_IFD_0),  env, TAG_FORWARDMATRIX2, writer);
1227        }
1228    }
1229
1230    {
1231        // Set camera neutral
1232        camera_metadata_entry entry =
1233            results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
1234        BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
1235        uint32_t cameraNeutral[entry.count * 2];
1236
1237        size_t ctr = 0;
1238        for(size_t i = 0; i < entry.count; ++i) {
1239            cameraNeutral[ctr++] =
1240                    static_cast<uint32_t>(entry.data.r[i].numerator);
1241            cameraNeutral[ctr++] =
1242                    static_cast<uint32_t>(entry.data.r[i].denominator);
1243        }
1244
1245        BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
1246                TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
1247    }
1248
1249    {
1250        // Setup data strips
1251        // TODO: Switch to tiled implementation.
1252        if (writer->addStrip(TIFF_IFD_0) != OK) {
1253            ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
1254            jniThrowException(env, "java/lang/IllegalStateException",
1255                    "Failed to setup strip tags.");
1256            return;
1257        }
1258    }
1259
1260    {
1261        // Setup default crop + crop origin tags
1262        uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
1263        uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
1264        if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
1265            uint32_t defaultCropOrigin[] = {margin, margin};
1266            uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
1267            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
1268                    TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
1269            BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
1270                    TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
1271        }
1272    }
1273
1274    {
1275        // Setup unique camera model tag
1276        char model[PROPERTY_VALUE_MAX];
1277        property_get("ro.product.model", model, "");
1278
1279        char manufacturer[PROPERTY_VALUE_MAX];
1280        property_get("ro.product.manufacturer", manufacturer, "");
1281
1282        char brand[PROPERTY_VALUE_MAX];
1283        property_get("ro.product.brand", brand, "");
1284
1285        String8 cameraModel(model);
1286        cameraModel += "-";
1287        cameraModel += manufacturer;
1288        cameraModel += "-";
1289        cameraModel += brand;
1290
1291        BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1292                reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
1293                TAG_UNIQUECAMERAMODEL, writer);
1294    }
1295
1296    {
1297        // Setup sensor noise model
1298        camera_metadata_entry entry =
1299            results.find(ANDROID_SENSOR_NOISE_PROFILE);
1300
1301        if (entry.count > 0) {
1302            BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, entry.count,
1303                    entry.data.d, TIFF_IFD_0), env,
1304                    TAG_NOISEPROFILE, writer);
1305        } else {
1306            ALOGW("%s: No noise profile found in result metadata.  Image quality may be reduced.",
1307                    __FUNCTION__);
1308        }
1309    }
1310
1311    {
1312        // Setup opcode List 2
1313        camera_metadata_entry entry1 =
1314                characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
1315
1316        uint32_t lsmWidth = 0;
1317        uint32_t lsmHeight = 0;
1318
1319        if (entry1.count != 0) {
1320            lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1321            lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1322        }
1323
1324        camera_metadata_entry entry2 =
1325                results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
1326
1327        if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
1328
1329            OpcodeListBuilder builder;
1330            status_t err = builder.addGainMapsForMetadata(lsmWidth,
1331                                                          lsmHeight,
1332                                                          0,
1333                                                          0,
1334                                                          imageHeight,
1335                                                          imageWidth,
1336                                                          opcodeCfaLayout,
1337                                                          entry2.data.f);
1338            if (err == OK) {
1339                size_t listSize = builder.getSize();
1340                uint8_t opcodeListBuf[listSize];
1341                err = builder.buildOpList(opcodeListBuf);
1342                if (err == OK) {
1343                    BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
1344                            TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1345                } else {
1346                    ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
1347                    jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
1348                }
1349            } else {
1350                ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1351                jniThrowRuntimeException(env, "failed to add lens shading map.");
1352            }
1353        } else {
1354            ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1355                    __FUNCTION__);
1356        }
1357    }
1358
1359    DngCreator_setNativeContext(env, thiz, nativeContext);
1360}
1361
1362static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1363    ALOGV("%s:", __FUNCTION__);
1364    DngCreator_setNativeContext(env, thiz, NULL);
1365}
1366
1367static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
1368    ALOGV("%s:", __FUNCTION__);
1369
1370    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1371    if (writer == NULL) {
1372        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1373        jniThrowException(env, "java/lang/AssertionError",
1374                "setOrientation called with uninitialized DngCreator");
1375        return;
1376    }
1377
1378    uint16_t orientation = static_cast<uint16_t>(orient);
1379    BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1380                TAG_ORIENTATION, writer);
1381
1382    // Set main image orientation also if in a separate IFD
1383    if (writer->hasIfd(TIFF_IFD_SUB1)) {
1384        BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1385                    TAG_ORIENTATION, writer);
1386    }
1387}
1388
1389static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
1390    ALOGV("%s:", __FUNCTION__);
1391
1392    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1393    if (writer == NULL) {
1394        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1395        jniThrowException(env, "java/lang/AssertionError",
1396                "setDescription called with uninitialized DngCreator");
1397        return;
1398    }
1399
1400    const char* desc = env->GetStringUTFChars(description, NULL);
1401    size_t len = strlen(desc) + 1;
1402
1403    if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1404            reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1405        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1406                "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1407    }
1408
1409    env->ReleaseStringUTFChars(description, desc);
1410}
1411
1412static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1413        jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
1414    ALOGV("%s:", __FUNCTION__);
1415
1416    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1417    if (writer == NULL) {
1418        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1419        jniThrowException(env, "java/lang/AssertionError",
1420                "setGpsTags called with uninitialized DngCreator");
1421        return;
1422    }
1423
1424    if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1425        if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1426            ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1427                    TIFF_IFD_0);
1428            jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1429            return;
1430        }
1431    }
1432
1433    const jsize GPS_VALUE_LENGTH = 6;
1434    jsize latLen = env->GetArrayLength(latTag);
1435    jsize longLen = env->GetArrayLength(longTag);
1436    jsize timeLen = env->GetArrayLength(timeTag);
1437    if (latLen != GPS_VALUE_LENGTH) {
1438        jniThrowException(env, "java/lang/IllegalArgumentException",
1439                "invalid latitude tag length");
1440        return;
1441    } else if (longLen != GPS_VALUE_LENGTH) {
1442        jniThrowException(env, "java/lang/IllegalArgumentException",
1443                "invalid longitude tag length");
1444        return;
1445    } else if (timeLen != GPS_VALUE_LENGTH) {
1446        jniThrowException(env, "java/lang/IllegalArgumentException",
1447                "invalid time tag length");
1448        return;
1449    }
1450
1451    uint32_t latitude[GPS_VALUE_LENGTH];
1452    uint32_t longitude[GPS_VALUE_LENGTH];
1453    uint32_t timestamp[GPS_VALUE_LENGTH];
1454
1455    env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1456            reinterpret_cast<jint*>(&latitude));
1457    env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1458            reinterpret_cast<jint*>(&longitude));
1459    env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1460            reinterpret_cast<jint*>(&timestamp));
1461
1462    const jsize GPS_REF_LENGTH = 2;
1463    const jsize GPS_DATE_LENGTH = 11;
1464    uint8_t latitudeRef[GPS_REF_LENGTH];
1465    uint8_t longitudeRef[GPS_REF_LENGTH];
1466    uint8_t date[GPS_DATE_LENGTH];
1467
1468    env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1469    latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1470    env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1471    longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1472
1473    env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1474    date[GPS_DATE_LENGTH - 1] = '\0';
1475
1476    {
1477        uint8_t version[] = {2, 3, 0, 0};
1478        BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1479                TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1480    }
1481
1482    {
1483        BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1484                TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1485    }
1486
1487    {
1488        BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1489                TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1490    }
1491
1492    {
1493        BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1494                TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1495    }
1496
1497    {
1498        BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1499                TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1500    }
1501
1502    {
1503        BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1504                TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1505    }
1506
1507    {
1508        BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1509                TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1510    }
1511}
1512
1513static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1514        jint height) {
1515    ALOGV("%s:", __FUNCTION__);
1516
1517    NativeContext* context = DngCreator_getNativeContext(env, thiz);
1518    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1519    if (writer == NULL || context == NULL) {
1520        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1521        jniThrowException(env, "java/lang/AssertionError",
1522                "setThumbnail called with uninitialized DngCreator");
1523        return;
1524    }
1525
1526    size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1527    jlong capacity = env->GetDirectBufferCapacity(buffer);
1528    if (capacity != fullSize) {
1529        jniThrowExceptionFmt(env, "java/lang/AssertionError",
1530                "Invalid size %d for thumbnail, expected size was %d",
1531                capacity, fullSize);
1532        return;
1533    }
1534
1535    uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1536    if (pixelBytes == NULL) {
1537        ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1538        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1539        return;
1540    }
1541
1542    if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1543        if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1544            ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1545                    TIFF_IFD_0);
1546            jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1547            return;
1548        }
1549
1550        Vector<uint16_t> tagsToMove;
1551        tagsToMove.add(TAG_ORIENTATION);
1552        tagsToMove.add(TAG_NEWSUBFILETYPE);
1553        tagsToMove.add(TAG_BITSPERSAMPLE);
1554        tagsToMove.add(TAG_COMPRESSION);
1555        tagsToMove.add(TAG_IMAGEWIDTH);
1556        tagsToMove.add(TAG_IMAGELENGTH);
1557        tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1558        tagsToMove.add(TAG_BLACKLEVEL);
1559        tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1560        tagsToMove.add(TAG_SAMPLESPERPIXEL);
1561        tagsToMove.add(TAG_PLANARCONFIGURATION);
1562        tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1563        tagsToMove.add(TAG_CFAPATTERN);
1564        tagsToMove.add(TAG_CFAPLANECOLOR);
1565        tagsToMove.add(TAG_CFALAYOUT);
1566        tagsToMove.add(TAG_XRESOLUTION);
1567        tagsToMove.add(TAG_YRESOLUTION);
1568        tagsToMove.add(TAG_RESOLUTIONUNIT);
1569        tagsToMove.add(TAG_WHITELEVEL);
1570        tagsToMove.add(TAG_DEFAULTSCALE);
1571        tagsToMove.add(TAG_ROWSPERSTRIP);
1572        tagsToMove.add(TAG_STRIPBYTECOUNTS);
1573        tagsToMove.add(TAG_STRIPOFFSETS);
1574        tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1575        tagsToMove.add(TAG_DEFAULTCROPSIZE);
1576        tagsToMove.add(TAG_OPCODELIST2);
1577
1578        if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1579            jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1580            return;
1581        }
1582
1583        // Make sure both IFDs get the same orientation tag
1584        sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1585        if (orientEntry != NULL) {
1586            writer->addEntry(orientEntry, TIFF_IFD_0);
1587        }
1588    }
1589
1590    // Setup thumbnail tags
1591
1592    {
1593        // Set photometric interpretation
1594        uint16_t interpretation = 2; // RGB
1595        BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1596                TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1597    }
1598
1599    {
1600        // Set planar configuration
1601        uint16_t config = 1; // Chunky
1602        BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1603                env, TAG_PLANARCONFIGURATION, writer);
1604    }
1605
1606    {
1607        // Set samples per pixel
1608        uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1609        BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1610                env, TAG_SAMPLESPERPIXEL, writer);
1611    }
1612
1613    {
1614        // Set bits per sample
1615        uint16_t bits = BITS_PER_RGB_SAMPLE;
1616        BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1617                TAG_BITSPERSAMPLE, writer);
1618    }
1619
1620    {
1621        // Set subfiletype
1622        uint32_t subfileType = 1; // Thumbnail image
1623        BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1624                TAG_NEWSUBFILETYPE, writer);
1625    }
1626
1627    {
1628        // Set compression
1629        uint16_t compression = 1; // None
1630        BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1631                TAG_COMPRESSION, writer);
1632    }
1633
1634    {
1635        // Set dimensions
1636        uint32_t uWidth = static_cast<uint32_t>(width);
1637        uint32_t uHeight = static_cast<uint32_t>(height);
1638        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1639                TAG_IMAGEWIDTH, writer);
1640        BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1641                TAG_IMAGELENGTH, writer);
1642    }
1643
1644    {
1645        // x resolution
1646        uint32_t xres[] = { 72, 1 }; // default 72 ppi
1647        BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1648                env, TAG_XRESOLUTION, writer);
1649
1650        // y resolution
1651        uint32_t yres[] = { 72, 1 }; // default 72 ppi
1652        BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1653                env, TAG_YRESOLUTION, writer);
1654
1655        uint16_t unit = 2; // inches
1656        BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1657                env, TAG_RESOLUTIONUNIT, writer);
1658    }
1659
1660    {
1661        // Setup data strips
1662        if (writer->addStrip(TIFF_IFD_0) != OK) {
1663            ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1664            jniThrowException(env, "java/lang/IllegalStateException",
1665                    "Failed to setup thumbnail strip tags.");
1666            return;
1667        }
1668        if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1669            ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1670            jniThrowException(env, "java/lang/IllegalStateException",
1671                    "Failed to setup main image strip tags.");
1672            return;
1673        }
1674    }
1675
1676    if (!context->setThumbnail(pixelBytes, width, height)) {
1677        jniThrowException(env, "java/lang/IllegalStateException",
1678                "Failed to set thumbnail.");
1679        return;
1680    }
1681}
1682
1683// TODO: Refactor out common preamble for the two nativeWrite methods.
1684static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
1685        jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1686        jboolean isDirect) {
1687    ALOGV("%s:", __FUNCTION__);
1688    ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d,"
1689              " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset);
1690    uint32_t rStride = static_cast<uint32_t>(rowStride);
1691    uint32_t pStride = static_cast<uint32_t>(pixStride);
1692    uint32_t uWidth = static_cast<uint32_t>(width);
1693    uint32_t uHeight = static_cast<uint32_t>(height);
1694    uint64_t uOffset = static_cast<uint64_t>(offset);
1695
1696    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1697    if(env->ExceptionCheck()) {
1698        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1699        return;
1700    }
1701
1702    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1703    NativeContext* context = DngCreator_getNativeContext(env, thiz);
1704    if (writer == NULL || context == NULL) {
1705        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1706        jniThrowException(env, "java/lang/AssertionError",
1707                "Write called with uninitialized DngCreator");
1708        return;
1709    }
1710
1711    // Validate DNG header
1712    if (!validateDngHeader(env, writer, width, height)) {
1713        return;
1714    }
1715
1716    sp<JniInputByteBuffer> inBuf;
1717    Vector<StripSource*> sources;
1718    sp<DirectStripSource> thumbnailSource;
1719    uint32_t targetIfd = TIFF_IFD_0;
1720
1721    bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1722
1723    if (hasThumbnail) {
1724        ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1725        uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1726        uint32_t thumbWidth = context->getThumbnailWidth();
1727        thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1728                thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1729                bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1730                SAMPLES_PER_RGB_PIXEL);
1731        sources.add(thumbnailSource.get());
1732        targetIfd = TIFF_IFD_SUB1;
1733    }
1734
1735    if (isDirect) {
1736        size_t fullSize = rStride * uHeight;
1737        jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1738        if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1739            jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1740                    "Invalid size %d for Image, size given in metadata is %d at current stride",
1741                    capacity, fullSize);
1742            return;
1743        }
1744
1745        uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1746        if (pixelBytes == NULL) {
1747            ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1748            jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1749            return;
1750        }
1751
1752        ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1753        DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1754                rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1755        sources.add(&stripSource);
1756
1757        status_t ret = OK;
1758        if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1759            ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1760            if (!env->ExceptionCheck()) {
1761                jniThrowExceptionFmt(env, "java/io/IOException",
1762                        "Encountered error %d while writing file.", ret);
1763            }
1764            return;
1765        }
1766    } else {
1767        inBuf = new JniInputByteBuffer(env, inBuffer);
1768
1769        ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1770        InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1771                 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1772        sources.add(&stripSource);
1773
1774        status_t ret = OK;
1775        if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1776            ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1777            if (!env->ExceptionCheck()) {
1778                jniThrowExceptionFmt(env, "java/io/IOException",
1779                        "Encountered error %d while writing file.", ret);
1780            }
1781            return;
1782        }
1783    }
1784}
1785
1786static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
1787        jobject inStream, jint width, jint height, jlong offset) {
1788    ALOGV("%s:", __FUNCTION__);
1789
1790    uint32_t rowStride = width * BYTES_PER_SAMPLE;
1791    uint32_t pixStride = BYTES_PER_SAMPLE;
1792    uint32_t uWidth = static_cast<uint32_t>(width);
1793    uint32_t uHeight = static_cast<uint32_t>(height);
1794    uint64_t uOffset = static_cast<uint32_t>(offset);
1795
1796    ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u,"
1797              "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride,
1798              offset);
1799
1800    sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1801    if(env->ExceptionCheck()) {
1802        ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1803        return;
1804    }
1805
1806    TiffWriter* writer = DngCreator_getCreator(env, thiz);
1807    NativeContext* context = DngCreator_getNativeContext(env, thiz);
1808    if (writer == NULL || context == NULL) {
1809        ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1810        jniThrowException(env, "java/lang/AssertionError",
1811                "Write called with uninitialized DngCreator");
1812        return;
1813    }
1814
1815    // Validate DNG header
1816    if (!validateDngHeader(env, writer, width, height)) {
1817        return;
1818    }
1819
1820    sp<DirectStripSource> thumbnailSource;
1821    uint32_t targetIfd = TIFF_IFD_0;
1822    bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1823    Vector<StripSource*> sources;
1824
1825    if (hasThumbnail) {
1826        ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1827        uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1828        uint32_t width = context->getThumbnailWidth();
1829        thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1830                width, context->getThumbnailHeight(), bytesPerPixel,
1831                bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1832                SAMPLES_PER_RGB_PIXEL);
1833        sources.add(thumbnailSource.get());
1834        targetIfd = TIFF_IFD_SUB1;
1835    }
1836
1837    sp<JniInputStream> in = new JniInputStream(env, inStream);
1838
1839    ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1840    InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
1841             rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1842    sources.add(&stripSource);
1843
1844    status_t ret = OK;
1845    if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1846        ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1847        if (!env->ExceptionCheck()) {
1848            jniThrowExceptionFmt(env, "java/io/IOException",
1849                    "Encountered error %d while writing file.", ret);
1850        }
1851        return;
1852    }
1853}
1854
1855} /*extern "C" */
1856
1857static JNINativeMethod gDngCreatorMethods[] = {
1858    {"nativeClassInit",        "()V", (void*) DngCreator_nativeClassInit},
1859    {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
1860            "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1861            (void*) DngCreator_init},
1862    {"nativeDestroy",           "()V",      (void*) DngCreator_destroy},
1863    {"nativeSetOrientation",    "(I)V",     (void*) DngCreator_nativeSetOrientation},
1864    {"nativeSetDescription",    "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
1865    {"nativeSetGpsTags",    "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
1866            (void*) DngCreator_nativeSetGpsTags},
1867    {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
1868    {"nativeWriteImage",        "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
1869            (void*) DngCreator_nativeWriteImage},
1870    {"nativeWriteInputStream",    "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
1871            (void*) DngCreator_nativeWriteInputStream},
1872};
1873
1874int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
1875    return AndroidRuntime::registerNativeMethods(env,
1876                   "android/hardware/camera2/DngCreator", gDngCreatorMethods,
1877                   NELEM(gDngCreatorMethods));
1878}
1879