blob: 29051f14c72ffaed5ce80065412d073833fe8fec [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
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
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -080017//#define LOG_NDEBUG 0
Ruben Brunkf967a542014-04-28 16:31:11 -070018#define LOG_TAG "DngCreator_JNI"
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <string.h>
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070021#include <algorithm>
22#include <memory>
Ruben Brunk9ce22a02015-08-03 12:40:11 -070023#include <vector>
Emilian Peev7ca13712017-04-05 15:42:22 +010024#include <cmath>
Dan Albert46d84442014-11-18 16:07:51 -080025
Tom Cherry93099e42017-10-11 13:44:21 -070026#include <android-base/properties.h>
Dan Albert46d84442014-11-18 16:07:51 -080027#include <utils/Log.h>
28#include <utils/Errors.h>
29#include <utils/StrongPointer.h>
30#include <utils/RefBase.h>
31#include <utils/Vector.h>
Ruben Brunk20796122015-07-21 17:51:54 -070032#include <utils/String8.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070033#include <system/camera_metadata.h>
34#include <camera/CameraMetadata.h>
35#include <img_utils/DngUtils.h>
36#include <img_utils/TagDefinitions.h>
37#include <img_utils/TiffIfd.h>
38#include <img_utils/TiffWriter.h>
39#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070040#include <img_utils/Input.h>
41#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070042
Andreas Gampeed6b9df2014-11-20 22:02:20 -080043#include "core_jni_helpers.h"
Ruben Brunkb8df8e02014-06-02 22:59:45 -070044
Ruben Brunkf967a542014-04-28 16:31:11 -070045#include "android_runtime/AndroidRuntime.h"
46#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
47
48#include <jni.h>
Steven Moreland2279b252017-07-19 09:50:45 -070049#include <nativehelper/JNIHelp.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070050
51using namespace android;
52using namespace img_utils;
Tom Cherry93099e42017-10-11 13:44:21 -070053using android::base::GetProperty;
Ruben Brunkf967a542014-04-28 16:31:11 -070054
Ruben Brunk20796122015-07-21 17:51:54 -070055#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070056 if ((expr) != OK) { \
57 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070058 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070059 return false; \
Ruben Brunkf967a542014-04-28 16:31:11 -070060 }
61
Ruben Brunk20796122015-07-21 17:51:54 -070062
63#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
64 if ((expr) != OK) { \
65 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
66 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
67 return nullptr; \
68 }
69
70
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070071#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
72 if ((expr) != OK) { \
73 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
74 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
75 return -1; \
76 }
77
Ruben Brunk20796122015-07-21 17:51:54 -070078#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
Chih-Hung Hsieh3c22e002016-05-19 15:10:07 -070079 if ((entry).count == 0) { \
Ruben Brunkf967a542014-04-28 16:31:11 -070080 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070081 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070082 return nullptr; \
Ruben Brunkf967a542014-04-28 16:31:11 -070083 }
84
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -080085#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
86 if (expr) { \
87 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
88 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
89 return nullptr; \
90 }
91
Ruben Brunk20796122015-07-21 17:51:54 -070092
Ruben Brunkb6079002014-05-22 12:33:54 -070093#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070094
95static struct {
96 jfieldID mNativeContext;
97} gDngCreatorClassInfo;
98
99static struct {
100 jmethodID mWriteMethod;
101} gOutputStreamClassInfo;
102
Ruben Brunk47e91f22014-05-28 18:38:42 -0700103static struct {
104 jmethodID mReadMethod;
105 jmethodID mSkipMethod;
106} gInputStreamClassInfo;
107
108static struct {
109 jmethodID mGetMethod;
110} gInputByteBufferClassInfo;
111
Ruben Brunkf967a542014-04-28 16:31:11 -0700112enum {
113 BITS_PER_SAMPLE = 16,
114 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700115 BYTES_PER_RGB_PIXEL = 3,
116 BITS_PER_RGB_SAMPLE = 8,
117 BYTES_PER_RGB_SAMPLE = 1,
118 SAMPLES_PER_RGB_PIXEL = 3,
119 SAMPLES_PER_RAW_PIXEL = 1,
120 TIFF_IFD_0 = 0,
121 TIFF_IFD_SUB1 = 1,
122 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700123};
124
Ruben Brunk20796122015-07-21 17:51:54 -0700125
126/**
127 * POD container class for GPS tag data.
128 */
129class GpsData {
130public:
131 enum {
132 GPS_VALUE_LENGTH = 6,
133 GPS_REF_LENGTH = 2,
134 GPS_DATE_LENGTH = 11,
135 };
136
137 uint32_t mLatitude[GPS_VALUE_LENGTH];
138 uint32_t mLongitude[GPS_VALUE_LENGTH];
139 uint32_t mTimestamp[GPS_VALUE_LENGTH];
140 uint8_t mLatitudeRef[GPS_REF_LENGTH];
141 uint8_t mLongitudeRef[GPS_REF_LENGTH];
142 uint8_t mDate[GPS_DATE_LENGTH];
143};
144
Ruben Brunkf967a542014-04-28 16:31:11 -0700145// ----------------------------------------------------------------------------
146
Ruben Brunk47e91f22014-05-28 18:38:42 -0700147/**
148 * Container class for the persistent native context.
149 */
150
151class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700152public:
Ruben Brunk20796122015-07-21 17:51:54 -0700153 enum {
154 DATETIME_COUNT = 20,
155 };
156
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700157 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700158 virtual ~NativeContext();
159
160 TiffWriter* getWriter();
161
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700162 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
163 std::shared_ptr<const CameraMetadata> getResult() const;
164
Ruben Brunk20796122015-07-21 17:51:54 -0700165 uint32_t getThumbnailWidth() const;
166 uint32_t getThumbnailHeight() const;
167 const uint8_t* getThumbnail() const;
168 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700169
170 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
171
Ruben Brunk20796122015-07-21 17:51:54 -0700172 void setOrientation(uint16_t orientation);
173 uint16_t getOrientation() const;
174
175 void setDescription(const String8& desc);
176 String8 getDescription() const;
177 bool hasDescription() const;
178
179 void setGpsData(const GpsData& data);
180 GpsData getGpsData() const;
181 bool hasGpsData() const;
182
183 void setCaptureTime(const String8& formattedCaptureTime);
184 String8 getCaptureTime() const;
185 bool hasCaptureTime() const;
186
Ruben Brunk47e91f22014-05-28 18:38:42 -0700187private:
188 Vector<uint8_t> mCurrentThumbnail;
189 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700190 std::shared_ptr<CameraMetadata> mCharacteristics;
191 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700192 uint32_t mThumbnailWidth;
193 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700194 uint16_t mOrientation;
195 bool mThumbnailSet;
196 bool mGpsSet;
197 bool mDescriptionSet;
198 bool mCaptureTimeSet;
199 String8 mDescription;
200 GpsData mGpsData;
201 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700202};
203
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700204NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
205 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
206 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -0800207 mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
208 mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700209
210NativeContext::~NativeContext() {}
211
212TiffWriter* NativeContext::getWriter() {
213 return &mWriter;
214}
215
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700216std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
217 return mCharacteristics;
218}
219
220std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
221 return mResult;
222}
223
Ruben Brunk20796122015-07-21 17:51:54 -0700224uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700225 return mThumbnailWidth;
226}
227
Ruben Brunk20796122015-07-21 17:51:54 -0700228uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700229 return mThumbnailHeight;
230}
231
Ruben Brunk20796122015-07-21 17:51:54 -0700232const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700233 return mCurrentThumbnail.array();
234}
235
Ruben Brunk20796122015-07-21 17:51:54 -0700236bool NativeContext::hasThumbnail() const {
237 return mThumbnailSet;
238}
239
Ruben Brunk47e91f22014-05-28 18:38:42 -0700240bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
241 mThumbnailWidth = width;
242 mThumbnailHeight = height;
243
244 size_t size = BYTES_PER_RGB_PIXEL * width * height;
245 if (mCurrentThumbnail.resize(size) < 0) {
246 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
247 return false;
248 }
249
250 uint8_t* thumb = mCurrentThumbnail.editArray();
251 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700252 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700253 return true;
254}
255
Ruben Brunk20796122015-07-21 17:51:54 -0700256void NativeContext::setOrientation(uint16_t orientation) {
257 mOrientation = orientation;
258}
259
260uint16_t NativeContext::getOrientation() const {
261 return mOrientation;
262}
263
264void NativeContext::setDescription(const String8& desc) {
265 mDescription = desc;
266 mDescriptionSet = true;
267}
268
269String8 NativeContext::getDescription() const {
270 return mDescription;
271}
272
273bool NativeContext::hasDescription() const {
274 return mDescriptionSet;
275}
276
277void NativeContext::setGpsData(const GpsData& data) {
278 mGpsData = data;
279 mGpsSet = true;
280}
281
282GpsData NativeContext::getGpsData() const {
283 return mGpsData;
284}
285
286bool NativeContext::hasGpsData() const {
287 return mGpsSet;
288}
289
290void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
291 mFormattedCaptureTime = formattedCaptureTime;
292 mCaptureTimeSet = true;
293}
294
295String8 NativeContext::getCaptureTime() const {
296 return mFormattedCaptureTime;
297}
298
299bool NativeContext::hasCaptureTime() const {
300 return mCaptureTimeSet;
301}
302
Ruben Brunk47e91f22014-05-28 18:38:42 -0700303// End of NativeContext
304// ----------------------------------------------------------------------------
305
306/**
307 * Wrapper class for a Java OutputStream.
308 *
309 * This class is not intended to be used across JNI calls.
310 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700311class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
312public:
313 JniOutputStream(JNIEnv* env, jobject outStream);
314
315 virtual ~JniOutputStream();
316
317 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700318
Ruben Brunkf967a542014-04-28 16:31:11 -0700319 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700320
Ruben Brunkf967a542014-04-28 16:31:11 -0700321 status_t close();
322private:
323 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700324 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700325 };
326 jobject mOutputStream;
327 JNIEnv* mEnv;
328 jbyteArray mByteArray;
329};
330
331JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
332 mEnv(env) {
333 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700334 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700335 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
336 }
337}
338
339JniOutputStream::~JniOutputStream() {
340 mEnv->DeleteLocalRef(mByteArray);
341}
342
343status_t JniOutputStream::open() {
344 // Do nothing
345 return OK;
346}
347
348status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
349 while(count > 0) {
350 size_t len = BYTE_ARRAY_LENGTH;
351 len = (count > len) ? len : count;
352 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
353
354 if (mEnv->ExceptionCheck()) {
355 return BAD_VALUE;
356 }
357
358 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
359 0, len);
360
361 if (mEnv->ExceptionCheck()) {
362 return BAD_VALUE;
363 }
364
365 count -= len;
366 offset += len;
367 }
368 return OK;
369}
370
371status_t JniOutputStream::close() {
372 // Do nothing
373 return OK;
374}
375
Ruben Brunk47e91f22014-05-28 18:38:42 -0700376// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700377// ----------------------------------------------------------------------------
378
Ruben Brunk47e91f22014-05-28 18:38:42 -0700379/**
380 * Wrapper class for a Java InputStream.
381 *
382 * This class is not intended to be used across JNI calls.
383 */
384class JniInputStream : public Input, public LightRefBase<JniInputStream> {
385public:
386 JniInputStream(JNIEnv* env, jobject inStream);
387
388 status_t open();
389
390 status_t close();
391
392 ssize_t read(uint8_t* buf, size_t offset, size_t count);
393
394 ssize_t skip(size_t count);
395
396 virtual ~JniInputStream();
397private:
398 enum {
399 BYTE_ARRAY_LENGTH = 4096
400 };
401 jobject mInStream;
402 JNIEnv* mEnv;
403 jbyteArray mByteArray;
404
405};
406
407JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
408 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700409 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700410 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
411 }
412}
413
414JniInputStream::~JniInputStream() {
415 mEnv->DeleteLocalRef(mByteArray);
416}
417
418ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
419
420 jint realCount = BYTE_ARRAY_LENGTH;
421 if (count < BYTE_ARRAY_LENGTH) {
422 realCount = count;
423 }
424 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
425 realCount);
426
427 if (actual < 0) {
428 return NOT_ENOUGH_DATA;
429 }
430
431 if (mEnv->ExceptionCheck()) {
432 return BAD_VALUE;
433 }
434
435 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
436 if (mEnv->ExceptionCheck()) {
437 return BAD_VALUE;
438 }
439 return actual;
440}
441
442ssize_t JniInputStream::skip(size_t count) {
443 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
444 static_cast<jlong>(count));
445
446 if (mEnv->ExceptionCheck()) {
447 return BAD_VALUE;
448 }
449 if (actual < 0) {
450 return NOT_ENOUGH_DATA;
451 }
452 return actual;
453}
454
455status_t JniInputStream::open() {
456 // Do nothing
457 return OK;
458}
459
460status_t JniInputStream::close() {
461 // Do nothing
462 return OK;
463}
464
465// End of JniInputStream
466// ----------------------------------------------------------------------------
467
468/**
469 * Wrapper class for a non-direct Java ByteBuffer.
470 *
471 * This class is not intended to be used across JNI calls.
472 */
473class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
474public:
475 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
476
477 status_t open();
478
479 status_t close();
480
481 ssize_t read(uint8_t* buf, size_t offset, size_t count);
482
483 virtual ~JniInputByteBuffer();
484private:
485 enum {
486 BYTE_ARRAY_LENGTH = 4096
487 };
488 jobject mInBuf;
489 JNIEnv* mEnv;
490 jbyteArray mByteArray;
491};
492
493JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
494 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700495 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700496 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
497 }
498}
499
500JniInputByteBuffer::~JniInputByteBuffer() {
501 mEnv->DeleteLocalRef(mByteArray);
502}
503
504ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
505 jint realCount = BYTE_ARRAY_LENGTH;
506 if (count < BYTE_ARRAY_LENGTH) {
507 realCount = count;
508 }
509
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700510 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
511 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800512 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700513
514 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800515 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700516 return BAD_VALUE;
517 }
518
519 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
520 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800521 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700522 return BAD_VALUE;
523 }
524 return realCount;
525}
526
527status_t JniInputByteBuffer::open() {
528 // Do nothing
529 return OK;
530}
531
532status_t JniInputByteBuffer::close() {
533 // Do nothing
534 return OK;
535}
536
537// End of JniInputByteBuffer
538// ----------------------------------------------------------------------------
539
540/**
541 * StripSource subclass for Input types.
542 *
543 * This class is not intended to be used across JNI calls.
544 */
545
546class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
547public:
548 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
549 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
550 uint32_t samplesPerPixel);
551
552 virtual ~InputStripSource();
553
554 virtual status_t writeToStream(Output& stream, uint32_t count);
555
556 virtual uint32_t getIfd() const;
557protected:
558 uint32_t mIfd;
559 Input* mInput;
560 uint32_t mWidth;
561 uint32_t mHeight;
562 uint32_t mPixStride;
563 uint32_t mRowStride;
564 uint64_t mOffset;
565 JNIEnv* mEnv;
566 uint32_t mBytesPerSample;
567 uint32_t mSamplesPerPixel;
568};
569
570InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
571 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
572 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
573 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
574 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
575 mSamplesPerPixel(samplesPerPixel) {}
576
577InputStripSource::~InputStripSource() {}
578
579status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700580 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700581 jlong offset = mOffset;
582
583 if (fullSize != count) {
584 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
585 fullSize);
586 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
587 return BAD_VALUE;
588 }
589
590 // Skip offset
591 while (offset > 0) {
592 ssize_t skipped = mInput->skip(offset);
593 if (skipped <= 0) {
594 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
595 jniThrowExceptionFmt(mEnv, "java/io/IOException",
596 "Early EOF encountered in skip, not enough pixel data for image of size %u",
597 fullSize);
598 skipped = NOT_ENOUGH_DATA;
599 } else {
600 if (!mEnv->ExceptionCheck()) {
601 jniThrowException(mEnv, "java/io/IOException",
602 "Error encountered while skip bytes in input stream.");
603 }
604 }
605
606 return skipped;
607 }
608 offset -= skipped;
609 }
610
611 Vector<uint8_t> row;
612 if (row.resize(mRowStride) < 0) {
613 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
614 return BAD_VALUE;
615 }
616
617 uint8_t* rowBytes = row.editArray();
618
619 for (uint32_t i = 0; i < mHeight; ++i) {
620 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800621 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700622
623 while (rowFillAmt < mRowStride) {
624 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
625 if (bytesRead <= 0) {
626 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800627 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
628 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700629 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800630 "Early EOF encountered, not enough pixel data for image of size %"
631 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700632 bytesRead = NOT_ENOUGH_DATA;
633 } else {
634 if (!mEnv->ExceptionCheck()) {
635 jniThrowException(mEnv, "java/io/IOException",
636 "Error encountered while reading");
637 }
638 }
639 return bytesRead;
640 }
641 rowFillAmt += bytesRead;
642 rowSize -= bytesRead;
643 }
644
645 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
646 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
647
648 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
649 mEnv->ExceptionCheck()) {
650 if (!mEnv->ExceptionCheck()) {
651 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
652 }
653 return BAD_VALUE;
654 }
655 } else {
656 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
657 jniThrowException(mEnv, "java/lang/IllegalStateException",
658 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
659 return BAD_VALUE;
660
661 // TODO: Add support for non-contiguous pixels if needed.
662 }
663 }
664 return OK;
665}
666
667uint32_t InputStripSource::getIfd() const {
668 return mIfd;
669}
670
671// End of InputStripSource
672// ----------------------------------------------------------------------------
673
674/**
675 * StripSource subclass for direct buffer types.
676 *
677 * This class is not intended to be used across JNI calls.
678 */
679
680class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
681public:
682 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
683 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
684 uint32_t bytesPerSample, uint32_t samplesPerPixel);
685
686 virtual ~DirectStripSource();
687
688 virtual status_t writeToStream(Output& stream, uint32_t count);
689
690 virtual uint32_t getIfd() const;
691protected:
692 uint32_t mIfd;
693 const uint8_t* mPixelBytes;
694 uint32_t mWidth;
695 uint32_t mHeight;
696 uint32_t mPixStride;
697 uint32_t mRowStride;
698 uint16_t mOffset;
699 JNIEnv* mEnv;
700 uint32_t mBytesPerSample;
701 uint32_t mSamplesPerPixel;
702};
703
704DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
705 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
706 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
707 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
708 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
709 mSamplesPerPixel(samplesPerPixel) {}
710
711DirectStripSource::~DirectStripSource() {}
712
713status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700714 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700715
716 if (fullSize != count) {
717 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
718 fullSize);
719 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
720 return BAD_VALUE;
721 }
722
Ruben Brunk20796122015-07-21 17:51:54 -0700723
Ruben Brunk47e91f22014-05-28 18:38:42 -0700724 if (mPixStride == mBytesPerSample * mSamplesPerPixel
725 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
726 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
727
728 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
729 if (!mEnv->ExceptionCheck()) {
730 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
731 }
732 return BAD_VALUE;
733 }
734 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
735 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
736
737 for (size_t i = 0; i < mHeight; ++i) {
738 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
739 mEnv->ExceptionCheck()) {
740 if (!mEnv->ExceptionCheck()) {
741 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
742 }
743 return BAD_VALUE;
744 }
745 }
746 } else {
747 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
748
749 jniThrowException(mEnv, "java/lang/IllegalStateException",
750 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
751 return BAD_VALUE;
752
753 // TODO: Add support for non-contiguous pixels if needed.
754 }
755 return OK;
756
757}
758
759uint32_t DirectStripSource::getIfd() const {
760 return mIfd;
761}
762
763// End of DirectStripSource
764// ----------------------------------------------------------------------------
765
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700766/**
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700767 * Calculate the default crop relative to the "active area" of the image sensor (this active area
768 * will always be the pre-correction active area rectangle), and set this.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700769 */
770static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700771 sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700772
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700773 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700774 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700775 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
776 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
777
Ruben Brunk20796122015-07-21 17:51:54 -0700778 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
779
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700780 if (width < margin * 2 || height < margin * 2) {
781 ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too"
782 "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width);
783 jniThrowException(env, "java/lang/IllegalStateException",
784 "Pre-correction active area is too small.");
785 return BAD_VALUE;
Ruben Brunk20796122015-07-21 17:51:54 -0700786 }
787
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700788 uint32_t defaultCropOrigin[] = {margin, margin};
789 uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin,
790 height - defaultCropOrigin[1] - margin};
791
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700792 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
793 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
794 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
795 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
796
797 return OK;
798}
799
Ruben Brunk20796122015-07-21 17:51:54 -0700800static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700801 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700802 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700803 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700804 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700805 return false;
806 }
807
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700808 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700809 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700810 "Image height %d is invalid", height);
811 return false;
812 }
813
814 camera_metadata_ro_entry preCorrectionEntry =
815 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
816 camera_metadata_ro_entry pixelArrayEntry =
817 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
818
819 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
820 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
821 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
822 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
823
824 bool matchesPixelArray = (pWidth == width && pHeight == height);
825 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
826
Ruben Brunk20796122015-07-21 17:51:54 -0700827 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700828 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
829 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
830 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
831 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700832 return false;
833 }
834
835 return true;
836}
837
Ruben Brunk20796122015-07-21 17:51:54 -0700838static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700839 const Vector<uint16_t>& entries) {
840 for (size_t i = 0; i < entries.size(); ++i) {
841 uint16_t tagId = entries[i];
842 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700843 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700844 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
845 ifdFrom);
846 return BAD_VALUE;
847 }
848 if (writer->addEntry(entry, ifdTo) != OK) {
849 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
850 ifdFrom);
851 return BAD_VALUE;
852 }
853 writer->removeEntry(tagId, ifdFrom);
854 }
855 return OK;
856}
857
Ruben Brunkd70132c2014-08-22 16:24:49 -0700858/**
859 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
860 * Returns OK on success, or a negative error code if the CFA enum was invalid.
861 */
862static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
863 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
864 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
865 cfaEnum);
866 switch(cfa) {
867 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
868 cfaOut[0] = 0;
869 cfaOut[1] = 1;
870 cfaOut[2] = 1;
871 cfaOut[3] = 2;
872 break;
873 }
874 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
875 cfaOut[0] = 1;
876 cfaOut[1] = 0;
877 cfaOut[2] = 2;
878 cfaOut[3] = 1;
879 break;
880 }
881 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
882 cfaOut[0] = 1;
883 cfaOut[1] = 2;
884 cfaOut[2] = 0;
885 cfaOut[3] = 1;
886 break;
887 }
888 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
889 cfaOut[0] = 2;
890 cfaOut[1] = 1;
891 cfaOut[2] = 1;
892 cfaOut[3] = 0;
893 break;
894 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -0700895 // MONO and NIR are degenerate case of RGGB pattern: only Red channel
896 // will be used.
897 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO:
898 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR: {
899 cfaOut[0] = 0;
900 break;
901 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700902 default: {
903 return BAD_VALUE;
904 }
905 }
906 return OK;
907}
908
909/**
910 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
911 * RGGB for an unknown enum.
912 */
913static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
914 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
915 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
916 cfaEnum);
917 switch(cfa) {
918 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
919 return OpcodeListBuilder::CFA_RGGB;
920 }
921 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
922 return OpcodeListBuilder::CFA_GRBG;
923 }
924 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
925 return OpcodeListBuilder::CFA_GBRG;
926 }
927 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
928 return OpcodeListBuilder::CFA_BGGR;
929 }
930 default: {
931 return OpcodeListBuilder::CFA_RGGB;
932 }
933 }
934}
935
936/**
937 * For each color plane, find the corresponding noise profile coefficients given in the
938 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
939 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
940 *
941 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
942 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
943 * coefficients.
944 * numChannels - the number of noise profile coefficient pairs and color channels given in
945 * the perChannelNoiseProfile and cfa arguments, respectively.
946 * planeColors - the color planes in the noise profile output.
947 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
948 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
949 *
950 * returns OK, or a negative error code on failure.
951 */
952static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
953 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
954 /*out*/double* noiseProfile) {
955
956 for (size_t p = 0; p < numPlanes; ++p) {
957 size_t S = p * 2;
958 size_t O = p * 2 + 1;
959
960 noiseProfile[S] = 0;
961 noiseProfile[O] = 0;
962 bool uninitialized = true;
963 for (size_t c = 0; c < numChannels; ++c) {
964 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
965 noiseProfile[S] = perChannelNoiseProfile[c * 2];
966 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
967 uninitialized = false;
968 }
969 }
970 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800971 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
972 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700973 return BAD_VALUE;
974 }
975 }
976 return OK;
977}
978
Ruben Brunk47e91f22014-05-28 18:38:42 -0700979// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700980extern "C" {
981
Ruben Brunk47e91f22014-05-28 18:38:42 -0700982static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700983 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700984 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700985 gDngCreatorClassInfo.mNativeContext));
986}
987
Ruben Brunk47e91f22014-05-28 18:38:42 -0700988static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700989 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700990 NativeContext* current = DngCreator_getNativeContext(env, thiz);
991
Ruben Brunk20796122015-07-21 17:51:54 -0700992 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700993 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700994 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700995
Ruben Brunkf967a542014-04-28 16:31:11 -0700996 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700997 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700998 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700999
Ruben Brunkf967a542014-04-28 16:31:11 -07001000 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001001 reinterpret_cast<jlong>(context.get()));
1002}
1003
Ruben Brunkf967a542014-04-28 16:31:11 -07001004static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
1005 ALOGV("%s:", __FUNCTION__);
1006
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001007 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1008 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001009
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001010 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1011 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1012 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001013
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001014 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1015 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1016 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001017
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001018 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1019 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1020 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001021}
1022
1023static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001024 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001025 ALOGV("%s:", __FUNCTION__);
1026 CameraMetadata characteristics;
1027 CameraMetadata results;
1028 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1029 jniThrowException(env, "java/lang/AssertionError",
1030 "No native metadata defined for camera characteristics.");
1031 return;
1032 }
1033 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1034 jniThrowException(env, "java/lang/AssertionError",
1035 "No native metadata defined for capture results.");
1036 return;
1037 }
1038
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001039 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001040
1041 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1042
1043 size_t len = strlen(captureTime) + 1;
1044 if (len != NativeContext::DATETIME_COUNT) {
1045 jniThrowException(env, "java/lang/IllegalArgumentException",
1046 "Formatted capture time string length is not required 20 characters");
1047 return;
1048 }
1049
1050 nativeContext->setCaptureTime(String8(captureTime));
1051
1052 DngCreator_setNativeContext(env, thiz, nativeContext);
1053}
1054
1055static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1056 uint32_t imageHeight) {
1057
1058 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1059
1060 if (nativeContext == nullptr) {
1061 jniThrowException(env, "java/lang/AssertionError",
1062 "No native context, must call init before other operations.");
1063 return nullptr;
1064 }
1065
1066 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1067 CameraMetadata results = *(nativeContext->getResult());
1068
1069 sp<TiffWriter> writer = new TiffWriter();
1070
1071 uint32_t preWidth = 0;
1072 uint32_t preHeight = 0;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001073 uint8_t colorFilter = 0;
1074 bool isBayer = true;
Ruben Brunk20796122015-07-21 17:51:54 -07001075 {
1076 // Check dimensions
1077 camera_metadata_entry entry =
1078 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1079 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1080 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1081 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1082
1083 camera_metadata_entry pixelArrayEntry =
1084 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1085 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1086 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1087
1088 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1089 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1090 jniThrowException(env, "java/lang/AssertionError",
1091 "Height and width of imate buffer did not match height and width of"
1092 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1093 return nullptr;
1094 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001095
1096 camera_metadata_entry colorFilterEntry =
1097 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1098 colorFilter = colorFilterEntry.data.u8[0];
1099 camera_metadata_entry capabilitiesEntry =
1100 characteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
1101 size_t capsCount = capabilitiesEntry.count;
1102 uint8_t* caps = capabilitiesEntry.data.u8;
1103 if (std::find(caps, caps+capsCount, ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME)
1104 != caps+capsCount) {
1105 isBayer = false;
1106 } else if (colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO ||
1107 colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
1108 jniThrowException(env, "java/lang/AssertionError",
1109 "A camera device with MONO/NIR color filter must have MONOCHROME capability.");
1110 return nullptr;
1111 }
Ruben Brunk20796122015-07-21 17:51:54 -07001112 }
1113
Ruben Brunkf967a542014-04-28 16:31:11 -07001114 writer->addIfd(TIFF_IFD_0);
1115
1116 status_t err = OK;
1117
1118 const uint32_t samplesPerPixel = 1;
1119 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001120
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001121 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_NONE;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001122 uint8_t cfaPlaneColor[3] = {0, 1, 2};
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001123 camera_metadata_entry cfaEntry =
1124 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1125 BAIL_IF_EMPTY_RET_NULL_SP(cfaEntry, env, TAG_CFAPATTERN, writer);
1126 uint8_t cfaEnum = cfaEntry.data.u8[0];
Ruben Brunkf967a542014-04-28 16:31:11 -07001127
1128 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001129 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001130
1131 // Setup main image tags
1132
Ruben Brunkf967a542014-04-28 16:31:11 -07001133 {
1134 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001135 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001136 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1137 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001138 }
1139
1140 {
1141 // Set subfiletype
1142 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001143 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1144 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001145 }
1146
1147 {
1148 // Set bits per sample
1149 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001150 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001151 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001152 }
1153
1154 {
1155 // Set compression
1156 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001157 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1158 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001159 }
1160
1161 {
1162 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001163 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1164 env, TAG_IMAGEWIDTH, writer);
1165 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1166 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001167 }
1168
1169 {
1170 // Set photometric interpretation
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001171 uint16_t interpretation = isBayer ? 32803 /* CFA */ :
1172 34892; /* Linear Raw */;
Ruben Brunk20796122015-07-21 17:51:54 -07001173 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1174 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001175 }
1176
1177 {
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001178 uint16_t repeatDim[2] = {2, 2};
1179 if (!isBayer) {
1180 repeatDim[0] = repeatDim[1] = 1;
1181 }
1182 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1183 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
1184
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001185 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001186 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001187 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1188 uint32_t blackLevelRational[8] = {0};
1189 if (entry.count != 0) {
1190 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1191 for (size_t i = 0; i < entry.count; i++) {
1192 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1193 blackLevelRational[i * 2 + 1] = 100;
1194 }
1195 } else {
1196 // Fall back to static black level which is guaranteed
1197 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1198 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1199 for (size_t i = 0; i < entry.count; i++) {
1200 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1201 blackLevelRational[i * 2 + 1] = 1;
1202 }
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001203 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001204 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, repeatDim[0]*repeatDim[1],
1205 blackLevelRational, TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001206 }
1207
1208 {
1209 // Set samples per pixel
1210 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001211 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001212 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001213 }
1214
1215 {
1216 // Set planar configuration
1217 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001218 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1219 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001220 }
1221
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001222 // All CFA pattern tags are not necessary for monochrome cameras.
1223 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001224 // Set CFA pattern dimensions
1225 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001226 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1227 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001228
Ruben Brunkf967a542014-04-28 16:31:11 -07001229 // Set CFA pattern
Ruben Brunkd70132c2014-08-22 16:24:49 -07001230 const int cfaLength = 4;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001231 uint8_t cfa[cfaLength];
1232 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1233 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1234 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001235 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001236
Ruben Brunk20796122015-07-21 17:51:54 -07001237 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1238 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001239
1240 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001241
Ruben Brunkf967a542014-04-28 16:31:11 -07001242 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001243 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1244 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001245
Ruben Brunkf967a542014-04-28 16:31:11 -07001246 // Set CFA layout
1247 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001248 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001249 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001250 }
1251
1252 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001253 // image description
1254 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001255 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1256 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001257 }
1258
1259 {
1260 // make
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001261 // Use "" to represent unknown make as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001262 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1263 uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001264
Ruben Brunk20796122015-07-21 17:51:54 -07001265 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001266 reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE,
1267 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001268 }
1269
1270 {
1271 // model
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001272 // Use "" to represent unknown model as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001273 std::string model = GetProperty("ro.product.model", "");
1274 uint32_t count = static_cast<uint32_t>(model.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001275
Ruben Brunk20796122015-07-21 17:51:54 -07001276 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001277 reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL,
1278 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001279 }
1280
1281 {
1282 // x resolution
1283 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001284 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001285 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001286
1287 // y resolution
1288 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001289 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001290 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001291
1292 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001293 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001294 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001295 }
1296
1297 {
1298 // software
Tom Cherry93099e42017-10-11 13:44:21 -07001299 std::string software = GetProperty("ro.build.fingerprint", "");
1300 uint32_t count = static_cast<uint32_t>(software.size()) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001301 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001302 reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE,
1303 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001304 }
1305
Ruben Brunk20796122015-07-21 17:51:54 -07001306 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001307 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001308 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001309
Ruben Brunk20796122015-07-21 17:51:54 -07001310 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1311 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001312 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1313 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001314 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001315 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001316
1317 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001318 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1319 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001320 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1321 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001322 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001323 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001324 }
1325
1326 {
1327 // TIFF/EP standard id
1328 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001329 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001330 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001331 }
1332
1333 {
1334 // copyright
1335 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001336 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001337 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001338 }
1339
1340 {
1341 // exposure time
1342 camera_metadata_entry entry =
1343 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001344 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001345
1346 int64_t exposureTime = *(entry.data.i64);
1347
1348 if (exposureTime < 0) {
1349 // Should be unreachable
1350 jniThrowException(env, "java/lang/IllegalArgumentException",
1351 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001352 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001353 }
1354
1355 // Ensure exposure time doesn't overflow (for exposures > 4s)
1356 uint32_t denominator = 1000000000;
1357 while (exposureTime > UINT32_MAX) {
1358 exposureTime >>= 1;
1359 denominator >>= 1;
1360 if (denominator == 0) {
1361 // Should be unreachable
1362 jniThrowException(env, "java/lang/IllegalArgumentException",
1363 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001364 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001365 }
1366 }
1367
1368 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001369 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001370 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001371
1372 }
1373
1374 {
1375 // ISO speed ratings
1376 camera_metadata_entry entry =
1377 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001378 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001379
1380 int32_t tempIso = *(entry.data.i32);
1381 if (tempIso < 0) {
1382 jniThrowException(env, "java/lang/IllegalArgumentException",
1383 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001384 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001385 }
1386
1387 if (tempIso > UINT16_MAX) {
1388 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1389 tempIso = UINT16_MAX;
1390 }
1391
1392 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001393 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001394 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001395 }
1396
1397 {
Emilian Peev7ca13712017-04-05 15:42:22 +01001398 // Baseline exposure
1399 camera_metadata_entry entry =
1400 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
1401 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
1402
1403 // post RAW gain should be boostValue / 100
1404 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
1405 // Baseline exposure should be in EV units so log2(gain) =
1406 // log10(gain)/log10(2)
1407 double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
1408 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
1409 100 };
1410 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
1411 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
1412 }
1413
1414 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001415 // focal length
1416 camera_metadata_entry entry =
1417 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001418 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001419
1420 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001421 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001422 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001423 }
1424
1425 {
1426 // f number
1427 camera_metadata_entry entry =
1428 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001429 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001430
1431 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001432 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001433 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001434 }
1435
1436 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001437 // Set DNG version information
1438 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001439 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001440 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001441
1442 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001443 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1444 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001445 }
1446
1447 {
1448 // Set whitelevel
1449 camera_metadata_entry entry =
1450 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001451 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001452 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001453 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1454 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001455 }
1456
1457 {
1458 // Set default scale
1459 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001460 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1461 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001462 }
1463
1464 bool singleIlluminant = false;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001465 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001466 // Set calibration illuminants
1467 camera_metadata_entry entry1 =
1468 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001469 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001470 camera_metadata_entry entry2 =
1471 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1472 if (entry2.count == 0) {
1473 singleIlluminant = true;
1474 }
1475 uint16_t ref1 = entry1.data.u8[0];
1476
Ruben Brunk20796122015-07-21 17:51:54 -07001477 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001478 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001479
1480 if (!singleIlluminant) {
1481 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001482 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001483 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001484 }
1485 }
1486
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001487 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001488 // Set color transforms
1489 camera_metadata_entry entry1 =
1490 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001491 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001492
1493 int32_t colorTransform1[entry1.count * 2];
1494
1495 size_t ctr = 0;
1496 for(size_t i = 0; i < entry1.count; ++i) {
1497 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1498 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1499 }
1500
Ruben Brunk20796122015-07-21 17:51:54 -07001501 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1502 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001503
1504 if (!singleIlluminant) {
1505 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001506 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001507 int32_t colorTransform2[entry2.count * 2];
1508
1509 ctr = 0;
1510 for(size_t i = 0; i < entry2.count; ++i) {
1511 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1512 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1513 }
1514
Ruben Brunk20796122015-07-21 17:51:54 -07001515 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1516 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001517 }
1518 }
1519
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001520 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001521 // Set calibration transforms
1522 camera_metadata_entry entry1 =
1523 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001524 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001525
1526 int32_t calibrationTransform1[entry1.count * 2];
1527
1528 size_t ctr = 0;
1529 for(size_t i = 0; i < entry1.count; ++i) {
1530 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1531 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1532 }
1533
Ruben Brunk20796122015-07-21 17:51:54 -07001534 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001535 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001536
1537 if (!singleIlluminant) {
1538 camera_metadata_entry entry2 =
1539 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001540 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001541 int32_t calibrationTransform2[entry2.count * 2];
1542
1543 ctr = 0;
1544 for(size_t i = 0; i < entry2.count; ++i) {
1545 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1546 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1547 }
1548
Ruben Brunk20796122015-07-21 17:51:54 -07001549 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001550 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001551 }
1552 }
1553
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001554 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001555 // Set forward transforms
1556 camera_metadata_entry entry1 =
1557 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001558 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001559
1560 int32_t forwardTransform1[entry1.count * 2];
1561
1562 size_t ctr = 0;
1563 for(size_t i = 0; i < entry1.count; ++i) {
1564 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1565 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1566 }
1567
Ruben Brunk20796122015-07-21 17:51:54 -07001568 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1569 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001570
1571 if (!singleIlluminant) {
1572 camera_metadata_entry entry2 =
1573 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001574 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001575 int32_t forwardTransform2[entry2.count * 2];
1576
1577 ctr = 0;
1578 for(size_t i = 0; i < entry2.count; ++i) {
1579 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1580 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1581 }
1582
Ruben Brunk20796122015-07-21 17:51:54 -07001583 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1584 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001585 }
1586 }
1587
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001588 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001589 // Set camera neutral
1590 camera_metadata_entry entry =
1591 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001592 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001593 uint32_t cameraNeutral[entry.count * 2];
1594
1595 size_t ctr = 0;
1596 for(size_t i = 0; i < entry.count; ++i) {
1597 cameraNeutral[ctr++] =
1598 static_cast<uint32_t>(entry.data.r[i].numerator);
1599 cameraNeutral[ctr++] =
1600 static_cast<uint32_t>(entry.data.r[i].denominator);
1601 }
1602
Ruben Brunk20796122015-07-21 17:51:54 -07001603 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001604 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001605 }
1606
Ruben Brunkf967a542014-04-28 16:31:11 -07001607
1608 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001609 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001610 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001611 return nullptr;
1612 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001613 camera_metadata_entry entry =
1614 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001615 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001616 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1617 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1618 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1619 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001620
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001621 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1622 // relative to the pixel array.
1623 if (imageWidth == width && imageHeight == height) {
1624 xmin = 0;
1625 ymin = 0;
1626 }
1627
Ruben Brunk20796122015-07-21 17:51:54 -07001628 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1629 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1630 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001631 }
1632
1633 {
1634 // Setup unique camera model tag
Tom Cherry93099e42017-10-11 13:44:21 -07001635 std::string model = GetProperty("ro.product.model", "");
1636 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1637 std::string brand = GetProperty("ro.product.brand", "");
Ruben Brunkf967a542014-04-28 16:31:11 -07001638
Tom Cherry93099e42017-10-11 13:44:21 -07001639 String8 cameraModel(model.c_str());
Ruben Brunkf967a542014-04-28 16:31:11 -07001640 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001641 cameraModel += manufacturer.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001642 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001643 cameraModel += brand.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001644
Ruben Brunk20796122015-07-21 17:51:54 -07001645 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001646 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001647 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001648 }
1649
1650 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001651 // Setup sensor noise model
1652 camera_metadata_entry entry =
1653 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1654
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001655 const status_t numPlaneColors = isBayer ? 3 : 1;
1656 const status_t numCfaChannels = isBayer ? 4 : 1;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001657
1658 uint8_t cfaOut[numCfaChannels];
1659 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1660 jniThrowException(env, "java/lang/IllegalArgumentException",
1661 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001662 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001663 }
1664
1665 double noiseProfile[numPlaneColors * 2];
1666
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001667 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001668 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001669 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1670 "in characteristics, no noise profile tag written...",
1671 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001672 } else {
1673 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1674 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1675
Ruben Brunk20796122015-07-21 17:51:54 -07001676 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1677 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1678 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001679 } else {
1680 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1681 " tag written...", __FUNCTION__);
1682 }
1683 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001684 } else {
1685 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1686 __FUNCTION__);
1687 }
1688 }
1689
1690 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001691 // Set up opcode List 2
1692 OpcodeListBuilder builder;
1693 status_t err = OK;
1694
1695 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001696 camera_metadata_entry entry1 =
1697 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001698
1699 uint32_t lsmWidth = 0;
1700 uint32_t lsmHeight = 0;
1701
1702 if (entry1.count != 0) {
1703 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1704 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1705 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001706
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001707 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001708
Ruben Brunk20796122015-07-21 17:51:54 -07001709 camera_metadata_entry entry =
1710 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1711 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1712 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1713 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1714 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1715 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001716 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001717 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001718 err = builder.addGainMapsForMetadata(lsmWidth,
1719 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001720 0,
1721 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001722 height,
1723 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001724 opcodeCfaLayout,
1725 entry2.data.f);
1726 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001727 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1728 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001729 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001730 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001731 }
1732
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001733 // Hot pixel map is specific to bayer camera per DNG spec.
1734 if (isBayer) {
1735 // Set up bad pixel correction list
1736 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001737
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001738 if ((entry3.count % 2) != 0) {
1739 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1740 __FUNCTION__);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001741 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1742 return nullptr;
1743 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001744
1745 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1746 std::vector<uint32_t> v;
1747 for (size_t i = 0; i < entry3.count; i += 2) {
1748 int32_t x = entry3.data.i32[i];
1749 int32_t y = entry3.data.i32[i + 1];
1750 x -= static_cast<int32_t>(xmin);
1751 y -= static_cast<int32_t>(ymin);
1752 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1753 static_cast<uint32_t>(y) >= height) {
1754 continue;
1755 }
1756 v.push_back(x);
1757 v.push_back(y);
1758 }
1759 const uint32_t* badPixels = &v[0];
1760 uint32_t badPixelCount = v.size();
1761
1762 if (badPixelCount > 0) {
1763 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1764
1765 if (err != OK) {
1766 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1767 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1768 return nullptr;
1769 }
1770 }
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001771 }
1772
Sam Hasinoff58398342018-08-01 18:24:59 -07001773 if (builder.getCount() > 0) {
1774 size_t listSize = builder.getSize();
1775 uint8_t opcodeListBuf[listSize];
1776 err = builder.buildOpList(opcodeListBuf);
1777 if (err == OK) {
1778 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize,
1779 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1780 } else {
1781 ALOGE("%s: Could not build list of opcodes for lens shading map and bad pixel "
1782 "correction.", __FUNCTION__);
1783 jniThrowRuntimeException(env, "failed to construct opcode list for lens shading "
1784 "map and bad pixel correction");
1785 return nullptr;
1786 }
Ruben Brunkfe816622015-06-16 23:03:09 -07001787 }
1788 }
1789
1790 {
1791 // Set up opcode List 3
1792 OpcodeListBuilder builder;
1793 status_t err = OK;
1794
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001795 // Set up rectilinear distortion correction
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001796 float distortion[6] {1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
1797 bool gotDistortion = false;
1798
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001799 camera_metadata_entry entry4 =
1800 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1801
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001802 if (entry4.count == 5) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001803 float cx = entry4.data.f[/*c_x*/2];
1804 float cy = entry4.data.f[/*c_y*/3];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001805 // Assuming f_x = f_y, or at least close enough.
1806 // Also assuming s = 0, or at least close enough.
1807 float f = entry4.data.f[/*f_x*/0];
1808
1809 camera_metadata_entry entry3 =
1810 results.find(ANDROID_LENS_DISTORTION);
1811 if (entry3.count == 5) {
1812 gotDistortion = true;
Yin-Chia Yeh47849ab2018-06-04 09:23:14 -07001813 float m_x = std::fmaxf(preWidth-1 - cx, cx);
1814 float m_y = std::fmaxf(preHeight-1 - cy, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001815 float m_sq = m_x*m_x + m_y*m_y;
1816 float m = sqrtf(m_sq); // distance to farthest corner from optical center
1817 float f_sq = f * f;
1818 // Conversion factors from Camera2 K factors for new LENS_DISTORTION field
1819 // to DNG spec.
1820 //
1821 // Camera2 / OpenCV assume distortion is applied in a space where focal length
1822 // is factored out, while DNG assumes a normalized space where the distance
1823 // from optical center to the farthest corner is 1.
1824 // Scale from camera2 to DNG spec accordingly.
1825 // distortion[0] is always 1 with the new LENS_DISTORTION field.
1826 const double convCoeff[5] = {
1827 m_sq / f_sq,
1828 pow(m_sq, 2) / pow(f_sq, 2),
1829 pow(m_sq, 3) / pow(f_sq, 3),
1830 m / f,
1831 m / f
1832 };
1833 for (size_t i = 0; i < entry3.count; i++) {
1834 distortion[i+1] = convCoeff[i] * entry3.data.f[i];
1835 }
1836 } else {
1837 entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION);
1838 if (entry3.count == 6) {
1839 gotDistortion = true;
1840 // Conversion factors from Camera2 K factors to DNG spec. K factors:
1841 //
1842 // Note: these are necessary because our unit system assumes a
1843 // normalized max radius of sqrt(2), whereas the DNG spec's
1844 // WarpRectilinear opcode assumes a normalized max radius of 1.
1845 // Thus, each K coefficient must include the domain scaling
1846 // factor (the DNG domain is scaled by sqrt(2) to emulate the
1847 // domain used by the Camera2 specification).
1848 const double convCoeff[6] = {
1849 sqrt(2),
1850 2 * sqrt(2),
1851 4 * sqrt(2),
1852 8 * sqrt(2),
1853 2,
1854 2
1855 };
1856 for (size_t i = 0; i < entry3.count; i++) {
1857 distortion[i] = entry3.data.f[i] * convCoeff[i];
1858 }
1859 }
1860 }
1861 if (gotDistortion) {
1862 err = builder.addWarpRectilinearForMetadata(distortion, preWidth, preHeight, cx,
1863 cy);
1864 if (err != OK) {
1865 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1866 jniThrowRuntimeException(env, "failed to add distortion correction.");
1867 return nullptr;
1868 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001869 }
1870 }
1871
Sam Hasinoff58398342018-08-01 18:24:59 -07001872 if (builder.getCount() > 0) {
1873 size_t listSize = builder.getSize();
1874 uint8_t opcodeListBuf[listSize];
1875 err = builder.buildOpList(opcodeListBuf);
1876 if (err == OK) {
1877 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize,
1878 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
1879 } else {
1880 ALOGE("%s: Could not build list of opcodes for distortion correction.",
1881 __FUNCTION__);
1882 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1883 " correction");
1884 return nullptr;
1885 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001886 }
1887 }
1888
Ruben Brunk20796122015-07-21 17:51:54 -07001889 {
1890 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08001891 // Note: There's only one orientation field for the whole file, in IFD0
1892 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07001893 uint16_t orientation = nativeContext->getOrientation();
1894 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1895 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001896
Ruben Brunk47e91f22014-05-28 18:38:42 -07001897 }
1898
Ruben Brunk20796122015-07-21 17:51:54 -07001899 if (nativeContext->hasDescription()){
1900 // Set Description
1901 String8 description = nativeContext->getDescription();
1902 size_t len = description.bytes() + 1;
1903 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1904 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1905 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1906 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001907 }
1908 }
1909
Ruben Brunk20796122015-07-21 17:51:54 -07001910 if (nativeContext->hasGpsData()) {
1911 // Set GPS tags
1912 GpsData gpsData = nativeContext->getGpsData();
1913 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1914 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1915 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1916 TIFF_IFD_0);
1917 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1918 return nullptr;
1919 }
1920 }
1921
1922 {
1923 uint8_t version[] = {2, 3, 0, 0};
1924 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1925 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1926 }
1927
1928 {
1929 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1930 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1931 TAG_GPSLATITUDEREF, writer);
1932 }
1933
1934 {
1935 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1936 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1937 TAG_GPSLONGITUDEREF, writer);
1938 }
1939
1940 {
1941 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1942 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1943 }
1944
1945 {
1946 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1947 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1948 }
1949
1950 {
1951 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1952 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1953 }
1954
1955 {
1956 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1957 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1958 TAG_GPSDATESTAMP, writer);
1959 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001960 }
1961
Ruben Brunk47e91f22014-05-28 18:38:42 -07001962
Ruben Brunk20796122015-07-21 17:51:54 -07001963 if (nativeContext->hasThumbnail()) {
1964 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1965 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1966 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1967 TIFF_IFD_0);
1968 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1969 return nullptr;
1970 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001971 }
1972
1973 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001974 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001975 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001976 tagsToMove.add(TAG_BITSPERSAMPLE);
1977 tagsToMove.add(TAG_COMPRESSION);
1978 tagsToMove.add(TAG_IMAGEWIDTH);
1979 tagsToMove.add(TAG_IMAGELENGTH);
1980 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1981 tagsToMove.add(TAG_BLACKLEVEL);
1982 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1983 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1984 tagsToMove.add(TAG_PLANARCONFIGURATION);
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001985 if (isBayer) {
1986 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1987 tagsToMove.add(TAG_CFAPATTERN);
1988 tagsToMove.add(TAG_CFAPLANECOLOR);
1989 tagsToMove.add(TAG_CFALAYOUT);
1990 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001991 tagsToMove.add(TAG_XRESOLUTION);
1992 tagsToMove.add(TAG_YRESOLUTION);
1993 tagsToMove.add(TAG_RESOLUTIONUNIT);
1994 tagsToMove.add(TAG_WHITELEVEL);
1995 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001996 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1997 tagsToMove.add(TAG_DEFAULTCROPSIZE);
Emilian Peeva7503b92018-08-13 12:46:05 +01001998
1999 if (nullptr != writer->getEntry(TAG_OPCODELIST2, TIFF_IFD_0).get()) {
2000 tagsToMove.add(TAG_OPCODELIST2);
2001 }
2002
2003 if (nullptr != writer->getEntry(TAG_OPCODELIST3, TIFF_IFD_0).get()) {
2004 tagsToMove.add(TAG_OPCODELIST3);
2005 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002006
2007 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
2008 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07002009 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002010 }
2011
Ruben Brunk20796122015-07-21 17:51:54 -07002012 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07002013
Ruben Brunk20796122015-07-21 17:51:54 -07002014 {
2015 // Set photometric interpretation
2016 uint16_t interpretation = 2; // RGB
2017 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
2018 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002019 }
Ruben Brunk20796122015-07-21 17:51:54 -07002020
2021 {
2022 // Set planar configuration
2023 uint16_t config = 1; // Chunky
2024 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
2025 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
2026 }
2027
2028 {
2029 // Set samples per pixel
2030 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
2031 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
2032 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
2033 }
2034
2035 {
2036 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08002037 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
2038 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
2039 BAIL_IF_INVALID_RET_NULL_SP(
2040 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07002041 env, TAG_BITSPERSAMPLE, writer);
2042 }
2043
2044 {
2045 // Set subfiletype
2046 uint32_t subfileType = 1; // Thumbnail image
2047 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
2048 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
2049 }
2050
2051 {
2052 // Set compression
2053 uint16_t compression = 1; // None
2054 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
2055 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
2056 }
2057
2058 {
2059 // Set dimensions
2060 uint32_t uWidth = nativeContext->getThumbnailWidth();
2061 uint32_t uHeight = nativeContext->getThumbnailHeight();
2062 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
2063 env, TAG_IMAGEWIDTH, writer);
2064 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
2065 env, TAG_IMAGELENGTH, writer);
2066 }
2067
2068 {
2069 // x resolution
2070 uint32_t xres[] = { 72, 1 }; // default 72 ppi
2071 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
2072 env, TAG_XRESOLUTION, writer);
2073
2074 // y resolution
2075 uint32_t yres[] = { 72, 1 }; // default 72 ppi
2076 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
2077 env, TAG_YRESOLUTION, writer);
2078
2079 uint16_t unit = 2; // inches
2080 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
2081 env, TAG_RESOLUTIONUNIT, writer);
2082 }
2083 }
2084
2085 if (writer->addStrip(TIFF_IFD_0) != OK) {
2086 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
2087 jniThrowException(env, "java/lang/IllegalStateException",
2088 "Failed to setup thumbnail strip tags.");
2089 return nullptr;
2090 }
2091
2092 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002093 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
2094 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
2095 jniThrowException(env, "java/lang/IllegalStateException",
2096 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07002097 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002098 }
2099 }
Ruben Brunk20796122015-07-21 17:51:54 -07002100 return writer;
2101}
2102
2103static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2104 ALOGV("%s:", __FUNCTION__);
2105 DngCreator_setNativeContext(env, thiz, nullptr);
2106}
2107
2108static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2109 ALOGV("%s:", __FUNCTION__);
2110
2111 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2112 if (context == nullptr) {
2113 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2114 jniThrowException(env, "java/lang/AssertionError",
2115 "setOrientation called with uninitialized DngCreator");
2116 return;
2117 }
2118
2119 uint16_t orientation = static_cast<uint16_t>(orient);
2120 context->setOrientation(orientation);
2121}
2122
2123static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2124 ALOGV("%s:", __FUNCTION__);
2125
2126 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2127 if (context == nullptr) {
2128 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2129 jniThrowException(env, "java/lang/AssertionError",
2130 "setDescription called with uninitialized DngCreator");
2131 return;
2132 }
2133
2134 const char* desc = env->GetStringUTFChars(description, nullptr);
2135 context->setDescription(String8(desc));
2136 env->ReleaseStringUTFChars(description, desc);
2137}
2138
2139static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2140 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2141 ALOGV("%s:", __FUNCTION__);
2142
2143 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2144 if (context == nullptr) {
2145 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2146 jniThrowException(env, "java/lang/AssertionError",
2147 "setGpsTags called with uninitialized DngCreator");
2148 return;
2149 }
2150
2151 GpsData data;
2152
2153 jsize latLen = env->GetArrayLength(latTag);
2154 jsize longLen = env->GetArrayLength(longTag);
2155 jsize timeLen = env->GetArrayLength(timeTag);
2156 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2157 jniThrowException(env, "java/lang/IllegalArgumentException",
2158 "invalid latitude tag length");
2159 return;
2160 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2161 jniThrowException(env, "java/lang/IllegalArgumentException",
2162 "invalid longitude tag length");
2163 return;
2164 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2165 jniThrowException(env, "java/lang/IllegalArgumentException",
2166 "invalid time tag length");
2167 return;
2168 }
2169
2170 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2171 reinterpret_cast<jint*>(&data.mLatitude));
2172 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2173 reinterpret_cast<jint*>(&data.mLongitude));
2174 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2175 reinterpret_cast<jint*>(&data.mTimestamp));
2176
2177
2178 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2179 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2180 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2181 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2182 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2183 reinterpret_cast<char*>(&data.mDate));
2184 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2185
2186 context->setGpsData(data);
2187}
2188
2189static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2190 jint height) {
2191 ALOGV("%s:", __FUNCTION__);
2192
2193 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2194 if (context == nullptr) {
2195 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2196 jniThrowException(env, "java/lang/AssertionError",
2197 "setThumbnail called with uninitialized DngCreator");
2198 return;
2199 }
2200
2201 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2202 jlong capacity = env->GetDirectBufferCapacity(buffer);
2203 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2204 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2205 "Invalid size %d for thumbnail, expected size was %d",
2206 capacity, fullSize);
2207 return;
2208 }
2209
2210 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2211 if (pixelBytes == nullptr) {
2212 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2213 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2214 return;
2215 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002216
2217 if (!context->setThumbnail(pixelBytes, width, height)) {
2218 jniThrowException(env, "java/lang/IllegalStateException",
2219 "Failed to set thumbnail.");
2220 return;
2221 }
2222}
2223
2224// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002225static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002226 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2227 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002228 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002229 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2230 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2231 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002232 uint32_t rStride = static_cast<uint32_t>(rowStride);
2233 uint32_t pStride = static_cast<uint32_t>(pixStride);
2234 uint32_t uWidth = static_cast<uint32_t>(width);
2235 uint32_t uHeight = static_cast<uint32_t>(height);
2236 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002237
2238 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2239 if(env->ExceptionCheck()) {
2240 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2241 return;
2242 }
2243
Ruben Brunk47e91f22014-05-28 18:38:42 -07002244 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002245 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002246 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2247 jniThrowException(env, "java/lang/AssertionError",
2248 "Write called with uninitialized DngCreator");
2249 return;
2250 }
Ruben Brunk20796122015-07-21 17:51:54 -07002251 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002252
Ruben Brunk20796122015-07-21 17:51:54 -07002253 if (writer.get() == nullptr) {
2254 return;
2255 }
2256
2257 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002258 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002259 return;
2260 }
2261
Ruben Brunk47e91f22014-05-28 18:38:42 -07002262 sp<JniInputByteBuffer> inBuf;
2263 Vector<StripSource*> sources;
2264 sp<DirectStripSource> thumbnailSource;
2265 uint32_t targetIfd = TIFF_IFD_0;
2266
2267 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2268
2269 if (hasThumbnail) {
2270 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2271 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2272 uint32_t thumbWidth = context->getThumbnailWidth();
2273 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2274 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2275 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2276 SAMPLES_PER_RGB_PIXEL);
2277 sources.add(thumbnailSource.get());
2278 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002279 }
2280
Ruben Brunk47e91f22014-05-28 18:38:42 -07002281 if (isDirect) {
2282 size_t fullSize = rStride * uHeight;
2283 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2284 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2285 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2286 "Invalid size %d for Image, size given in metadata is %d at current stride",
2287 capacity, fullSize);
2288 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002289 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002290
Ruben Brunk47e91f22014-05-28 18:38:42 -07002291 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002292 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002293 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2294 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2295 return;
2296 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002297
Ruben Brunk47e91f22014-05-28 18:38:42 -07002298 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2299 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2300 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2301 sources.add(&stripSource);
2302
2303 status_t ret = OK;
2304 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2305 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002306 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002307 jniThrowExceptionFmt(env, "java/io/IOException",
2308 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002309 }
2310 return;
2311 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002312 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002313 inBuf = new JniInputByteBuffer(env, inBuffer);
2314
2315 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2316 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2317 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2318 sources.add(&stripSource);
2319
2320 status_t ret = OK;
2321 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2322 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2323 if (!env->ExceptionCheck()) {
2324 jniThrowExceptionFmt(env, "java/io/IOException",
2325 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002326 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002327 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002328 }
2329 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002330}
2331
2332static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002333 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002334 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002335
2336 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2337 uint32_t pixStride = BYTES_PER_SAMPLE;
2338 uint32_t uWidth = static_cast<uint32_t>(width);
2339 uint32_t uHeight = static_cast<uint32_t>(height);
2340 uint64_t uOffset = static_cast<uint32_t>(offset);
2341
Dan Albert46d84442014-11-18 16:07:51 -08002342 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2343 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2344 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002345
2346 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002347 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002348 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2349 return;
2350 }
2351
Ruben Brunk47e91f22014-05-28 18:38:42 -07002352 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002353 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002354 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2355 jniThrowException(env, "java/lang/AssertionError",
2356 "Write called with uninitialized DngCreator");
2357 return;
2358 }
Ruben Brunk20796122015-07-21 17:51:54 -07002359 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002360
Ruben Brunk20796122015-07-21 17:51:54 -07002361 if (writer.get() == nullptr) {
2362 return;
2363 }
2364
2365 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002366 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002367 return;
2368 }
2369
2370 sp<DirectStripSource> thumbnailSource;
2371 uint32_t targetIfd = TIFF_IFD_0;
2372 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2373 Vector<StripSource*> sources;
2374
2375 if (hasThumbnail) {
2376 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2377 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2378 uint32_t width = context->getThumbnailWidth();
2379 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2380 width, context->getThumbnailHeight(), bytesPerPixel,
2381 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2382 SAMPLES_PER_RGB_PIXEL);
2383 sources.add(thumbnailSource.get());
2384 targetIfd = TIFF_IFD_SUB1;
2385 }
2386
2387 sp<JniInputStream> in = new JniInputStream(env, inStream);
2388
2389 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2390 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2391 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2392 sources.add(&stripSource);
2393
2394 status_t ret = OK;
2395 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2396 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2397 if (!env->ExceptionCheck()) {
2398 jniThrowExceptionFmt(env, "java/io/IOException",
2399 "Encountered error %d while writing file.", ret);
2400 }
2401 return;
2402 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002403}
2404
2405} /*extern "C" */
2406
Daniel Micay76f6a862015-09-19 17:31:01 -04002407static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002408 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2409 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002410 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2411 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002412 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2413 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002414 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2415 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2416 (void*) DngCreator_nativeSetGpsTags},
2417 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2418 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002419 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002420 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002421 (void*) DngCreator_nativeWriteInputStream},
2422};
2423
Ruben Brunkb6079002014-05-22 12:33:54 -07002424int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002425 return RegisterMethodsOrDie(env,
2426 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002427}