• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
46 using namespace android;
47 using 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 
65 static struct {
66     jfieldID mNativeContext;
67 } gDngCreatorClassInfo;
68 
69 static struct {
70     jmethodID mWriteMethod;
71 } gOutputStreamClassInfo;
72 
73 static struct {
74     jmethodID mReadMethod;
75     jmethodID mSkipMethod;
76 } gInputStreamClassInfo;
77 
78 static struct {
79     jmethodID mGetMethod;
80 } gInputByteBufferClassInfo;
81 
82 enum {
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 
101 class NativeContext : public LightRefBase<NativeContext> {
102 
103 public:
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 
115 private:
116     Vector<uint8_t> mCurrentThumbnail;
117     TiffWriter mWriter;
118     uint32_t mThumbnailWidth;
119     uint32_t mThumbnailHeight;
120 };
121 
NativeContext()122 NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
123 
~NativeContext()124 NativeContext::~NativeContext() {}
125 
getWriter()126 TiffWriter* NativeContext::getWriter() {
127     return &mWriter;
128 }
129 
getThumbnailWidth()130 uint32_t NativeContext::getThumbnailWidth() {
131     return mThumbnailWidth;
132 }
133 
getThumbnailHeight()134 uint32_t NativeContext::getThumbnailHeight() {
135     return mThumbnailHeight;
136 }
137 
getThumbnail()138 const uint8_t* NativeContext::getThumbnail() {
139     return mCurrentThumbnail.array();
140 }
141 
setThumbnail(const uint8_t * buffer,uint32_t width,uint32_t height)142 bool 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  */
165 class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
166 public:
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();
176 private:
177     enum {
178         BYTE_ARRAY_LENGTH = 4096
179     };
180     jobject mOutputStream;
181     JNIEnv* mEnv;
182     jbyteArray mByteArray;
183 };
184 
JniOutputStream(JNIEnv * env,jobject outStream)185 JniOutputStream::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 
~JniOutputStream()193 JniOutputStream::~JniOutputStream() {
194     mEnv->DeleteLocalRef(mByteArray);
195 }
196 
open()197 status_t JniOutputStream::open() {
198     // Do nothing
199     return OK;
200 }
201 
write(const uint8_t * buf,size_t offset,size_t count)202 status_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 
close()225 status_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  */
238 class JniInputStream : public Input, public LightRefBase<JniInputStream> {
239 public:
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();
251 private:
252     enum {
253         BYTE_ARRAY_LENGTH = 4096
254     };
255     jobject mInStream;
256     JNIEnv* mEnv;
257     jbyteArray mByteArray;
258 
259 };
260 
JniInputStream(JNIEnv * env,jobject inStream)261 JniInputStream::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 
~JniInputStream()268 JniInputStream::~JniInputStream() {
269     mEnv->DeleteLocalRef(mByteArray);
270 }
271 
read(uint8_t * buf,size_t offset,size_t count)272 ssize_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 
skip(size_t count)296 ssize_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 
open()309 status_t JniInputStream::open() {
310     // Do nothing
311     return OK;
312 }
313 
close()314 status_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  */
327 class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
328 public:
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();
338 private:
339     enum {
340         BYTE_ARRAY_LENGTH = 4096
341     };
342     jobject mInBuf;
343     JNIEnv* mEnv;
344     jbyteArray mByteArray;
345 };
346 
JniInputByteBuffer(JNIEnv * env,jobject inBuf)347 JniInputByteBuffer::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 
~JniInputByteBuffer()354 JniInputByteBuffer::~JniInputByteBuffer() {
355     mEnv->DeleteLocalRef(mByteArray);
356 }
357 
read(uint8_t * buf,size_t offset,size_t count)358 ssize_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 
open()381 status_t JniInputByteBuffer::open() {
382     // Do nothing
383     return OK;
384 }
385 
close()386 status_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 
400 class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
401 public:
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;
411 protected:
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 
InputStripSource(JNIEnv * env,Input & input,uint32_t ifd,uint32_t width,uint32_t height,uint32_t pixStride,uint32_t rowStride,uint64_t offset,uint32_t bytesPerSample,uint32_t samplesPerPixel)424 InputStripSource::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 
~InputStripSource()431 InputStripSource::~InputStripSource() {}
432 
writeToStream(Output & stream,uint32_t count)433 status_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 
getIfd() const522 uint32_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 
535 class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
536 public:
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;
546 protected:
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 
DirectStripSource(JNIEnv * env,const uint8_t * pixelBytes,uint32_t ifd,uint32_t width,uint32_t height,uint32_t pixStride,uint32_t rowStride,uint64_t offset,uint32_t bytesPerSample,uint32_t samplesPerPixel)559 DirectStripSource::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 
~DirectStripSource()566 DirectStripSource::~DirectStripSource() {}
567 
writeToStream(Output & stream,uint32_t count)568 status_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 
getIfd() const613 uint32_t DirectStripSource::getIfd() const {
614     return mIfd;
615 }
616 
617 // End of DirectStripSource
618 // ----------------------------------------------------------------------------
619 
validateDngHeader(JNIEnv * env,TiffWriter * writer,jint width,jint height)620 static 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 
moveEntries(TiffWriter * writer,uint32_t ifdFrom,uint32_t ifdTo,const Vector<uint16_t> & entries)642 static 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  */
convertCFA(uint8_t cfaEnum,uint8_t * cfaOut)666 static 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  */
convertCFAEnumToOpcodeLayout(uint8_t cfaEnum)710 static 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  */
generateNoiseProfile(const double * perChannelNoiseProfile,uint8_t * cfa,size_t numChannels,const uint8_t * planeColors,size_t numPlanes,double * noiseProfile)749 static 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 // ----------------------------------------------------------------------------
776 extern "C" {
777 
DngCreator_getNativeContext(JNIEnv * env,jobject thiz)778 static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
779     ALOGV("%s:", __FUNCTION__);
780     return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
781             gDngCreatorClassInfo.mNativeContext));
782 }
783 
DngCreator_setNativeContext(JNIEnv * env,jobject thiz,sp<NativeContext> context)784 static 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 
DngCreator_getCreator(JNIEnv * env,jobject thiz)800 static 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 
DngCreator_nativeClassInit(JNIEnv * env,jclass clazz)809 static 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 
DngCreator_init(JNIEnv * env,jobject thiz,jobject characteristicsPtr,jobject resultsPtr,jstring formattedCaptureTime)837 static 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 
DngCreator_destroy(JNIEnv * env,jobject thiz)1480 static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1481     ALOGV("%s:", __FUNCTION__);
1482     DngCreator_setNativeContext(env, thiz, NULL);
1483 }
1484 
DngCreator_nativeSetOrientation(JNIEnv * env,jobject thiz,jint orient)1485 static 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 
DngCreator_nativeSetDescription(JNIEnv * env,jobject thiz,jstring description)1507 static 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 
DngCreator_nativeSetGpsTags(JNIEnv * env,jobject thiz,jintArray latTag,jstring latRef,jintArray longTag,jstring longRef,jstring dateTag,jintArray timeTag)1530 static 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 
DngCreator_nativeSetThumbnail(JNIEnv * env,jobject thiz,jobject buffer,jint width,jint height)1631 static 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.
DngCreator_nativeWriteImage(JNIEnv * env,jobject thiz,jobject outStream,jint width,jint height,jobject inBuffer,jint rowStride,jint pixStride,jlong offset,jboolean isDirect)1802 static 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 
DngCreator_nativeWriteInputStream(JNIEnv * env,jobject thiz,jobject outStream,jobject inStream,jint width,jint height,jlong offset)1904 static 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 
1975 static 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 
register_android_hardware_camera2_DngCreator(JNIEnv * env)1992 int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
1993     return AndroidRuntime::registerNativeMethods(env,
1994                    "android/hardware/camera2/DngCreator", gDngCreatorMethods,
1995                    NELEM(gDngCreatorMethods));
1996 }
1997