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