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