blob: c977437f7df275c774e653f3fa076f358d6c9b22 [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 }
895 default: {
896 return BAD_VALUE;
897 }
898 }
899 return OK;
900}
901
902/**
903 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
904 * RGGB for an unknown enum.
905 */
906static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
907 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
908 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
909 cfaEnum);
910 switch(cfa) {
911 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
912 return OpcodeListBuilder::CFA_RGGB;
913 }
914 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
915 return OpcodeListBuilder::CFA_GRBG;
916 }
917 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
918 return OpcodeListBuilder::CFA_GBRG;
919 }
920 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
921 return OpcodeListBuilder::CFA_BGGR;
922 }
923 default: {
924 return OpcodeListBuilder::CFA_RGGB;
925 }
926 }
927}
928
929/**
930 * For each color plane, find the corresponding noise profile coefficients given in the
931 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
932 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
933 *
934 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
935 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
936 * coefficients.
937 * numChannels - the number of noise profile coefficient pairs and color channels given in
938 * the perChannelNoiseProfile and cfa arguments, respectively.
939 * planeColors - the color planes in the noise profile output.
940 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
941 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
942 *
943 * returns OK, or a negative error code on failure.
944 */
945static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
946 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
947 /*out*/double* noiseProfile) {
948
949 for (size_t p = 0; p < numPlanes; ++p) {
950 size_t S = p * 2;
951 size_t O = p * 2 + 1;
952
953 noiseProfile[S] = 0;
954 noiseProfile[O] = 0;
955 bool uninitialized = true;
956 for (size_t c = 0; c < numChannels; ++c) {
957 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
958 noiseProfile[S] = perChannelNoiseProfile[c * 2];
959 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
960 uninitialized = false;
961 }
962 }
963 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800964 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
965 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700966 return BAD_VALUE;
967 }
968 }
969 return OK;
970}
971
Ruben Brunk47e91f22014-05-28 18:38:42 -0700972// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700973extern "C" {
974
Ruben Brunk47e91f22014-05-28 18:38:42 -0700975static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700976 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700977 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700978 gDngCreatorClassInfo.mNativeContext));
979}
980
Ruben Brunk47e91f22014-05-28 18:38:42 -0700981static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700982 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700983 NativeContext* current = DngCreator_getNativeContext(env, thiz);
984
Ruben Brunk20796122015-07-21 17:51:54 -0700985 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700986 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700987 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700988
Ruben Brunkf967a542014-04-28 16:31:11 -0700989 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700990 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700991 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700992
Ruben Brunkf967a542014-04-28 16:31:11 -0700993 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700994 reinterpret_cast<jlong>(context.get()));
995}
996
Ruben Brunkf967a542014-04-28 16:31:11 -0700997static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
998 ALOGV("%s:", __FUNCTION__);
999
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001000 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1001 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001002
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001003 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1004 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1005 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001006
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001007 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1008 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1009 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001010
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001011 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1012 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1013 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001014}
1015
1016static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001017 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001018 ALOGV("%s:", __FUNCTION__);
1019 CameraMetadata characteristics;
1020 CameraMetadata results;
1021 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1022 jniThrowException(env, "java/lang/AssertionError",
1023 "No native metadata defined for camera characteristics.");
1024 return;
1025 }
1026 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1027 jniThrowException(env, "java/lang/AssertionError",
1028 "No native metadata defined for capture results.");
1029 return;
1030 }
1031
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001032 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001033
1034 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1035
1036 size_t len = strlen(captureTime) + 1;
1037 if (len != NativeContext::DATETIME_COUNT) {
1038 jniThrowException(env, "java/lang/IllegalArgumentException",
1039 "Formatted capture time string length is not required 20 characters");
1040 return;
1041 }
1042
1043 nativeContext->setCaptureTime(String8(captureTime));
1044
1045 DngCreator_setNativeContext(env, thiz, nativeContext);
1046}
1047
1048static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1049 uint32_t imageHeight) {
1050
1051 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1052
1053 if (nativeContext == nullptr) {
1054 jniThrowException(env, "java/lang/AssertionError",
1055 "No native context, must call init before other operations.");
1056 return nullptr;
1057 }
1058
1059 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1060 CameraMetadata results = *(nativeContext->getResult());
1061
1062 sp<TiffWriter> writer = new TiffWriter();
1063
1064 uint32_t preWidth = 0;
1065 uint32_t preHeight = 0;
1066 {
1067 // Check dimensions
1068 camera_metadata_entry entry =
1069 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1070 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1071 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1072 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1073
1074 camera_metadata_entry pixelArrayEntry =
1075 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1076 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1077 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1078
1079 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1080 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1081 jniThrowException(env, "java/lang/AssertionError",
1082 "Height and width of imate buffer did not match height and width of"
1083 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1084 return nullptr;
1085 }
1086 }
1087
1088
Ruben Brunkf967a542014-04-28 16:31:11 -07001089
1090 writer->addIfd(TIFF_IFD_0);
1091
1092 status_t err = OK;
1093
1094 const uint32_t samplesPerPixel = 1;
1095 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001096
1097 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001098 uint8_t cfaPlaneColor[3] = {0, 1, 2};
1099 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001100
1101 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001102 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001103
1104 // Setup main image tags
1105
Ruben Brunkf967a542014-04-28 16:31:11 -07001106 {
1107 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001108 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001109 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1110 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001111 }
1112
1113 {
1114 // Set subfiletype
1115 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001116 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1117 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001118 }
1119
1120 {
1121 // Set bits per sample
1122 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001123 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001124 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001125 }
1126
1127 {
1128 // Set compression
1129 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001130 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1131 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001132 }
1133
1134 {
1135 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001136 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1137 env, TAG_IMAGEWIDTH, writer);
1138 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1139 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001140 }
1141
1142 {
1143 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -07001144 uint16_t interpretation = 32803; // CFA
Ruben Brunk20796122015-07-21 17:51:54 -07001145 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1146 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001147 }
1148
1149 {
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001150 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001151 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001152 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1153 uint32_t blackLevelRational[8] = {0};
1154 if (entry.count != 0) {
1155 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1156 for (size_t i = 0; i < entry.count; i++) {
1157 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1158 blackLevelRational[i * 2 + 1] = 100;
1159 }
1160 } else {
1161 // Fall back to static black level which is guaranteed
1162 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1163 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1164 for (size_t i = 0; i < entry.count; i++) {
1165 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1166 blackLevelRational[i * 2 + 1] = 1;
1167 }
1168
1169 }
1170 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
Ruben Brunk20796122015-07-21 17:51:54 -07001171 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001172
1173 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001174 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1175 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001176 }
1177
1178 {
1179 // Set samples per pixel
1180 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001181 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001182 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001183 }
1184
1185 {
1186 // Set planar configuration
1187 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001188 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1189 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001190 }
1191
1192 {
1193 // Set CFA pattern dimensions
1194 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001195 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1196 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001197 }
1198
1199 {
1200 // Set CFA pattern
1201 camera_metadata_entry entry =
1202 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk20796122015-07-21 17:51:54 -07001203 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001204
1205 const int cfaLength = 4;
1206 cfaEnum = entry.data.u8[0];
1207 uint8_t cfa[cfaLength];
1208 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1209 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1210 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001211 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001212
Ruben Brunk20796122015-07-21 17:51:54 -07001213 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1214 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001215
1216 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001217 }
1218
1219 {
1220 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001221 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1222 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001223 }
1224
1225 {
1226 // Set CFA layout
1227 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001228 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001229 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001230 }
1231
1232 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001233 // image description
1234 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001235 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1236 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001237 }
1238
1239 {
1240 // make
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001241 // Use "" to represent unknown make as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001242 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1243 uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001244
Ruben Brunk20796122015-07-21 17:51:54 -07001245 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001246 reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE,
1247 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001248 }
1249
1250 {
1251 // model
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001252 // Use "" to represent unknown model as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001253 std::string model = GetProperty("ro.product.model", "");
1254 uint32_t count = static_cast<uint32_t>(model.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001255
Ruben Brunk20796122015-07-21 17:51:54 -07001256 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001257 reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL,
1258 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001259 }
1260
1261 {
1262 // x resolution
1263 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001264 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001265 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001266
1267 // y resolution
1268 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001269 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001270 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001271
1272 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001273 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001274 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001275 }
1276
1277 {
1278 // software
Tom Cherry93099e42017-10-11 13:44:21 -07001279 std::string software = GetProperty("ro.build.fingerprint", "");
1280 uint32_t count = static_cast<uint32_t>(software.size()) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001281 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001282 reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE,
1283 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001284 }
1285
Ruben Brunk20796122015-07-21 17:51:54 -07001286 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001287 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001288 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001289
Ruben Brunk20796122015-07-21 17:51:54 -07001290 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1291 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001292 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1293 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001294 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001295 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001296
1297 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001298 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1299 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001300 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1301 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001302 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001303 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001304 }
1305
1306 {
1307 // TIFF/EP standard id
1308 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001309 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001310 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001311 }
1312
1313 {
1314 // copyright
1315 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001316 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001317 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001318 }
1319
1320 {
1321 // exposure time
1322 camera_metadata_entry entry =
1323 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001324 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001325
1326 int64_t exposureTime = *(entry.data.i64);
1327
1328 if (exposureTime < 0) {
1329 // Should be unreachable
1330 jniThrowException(env, "java/lang/IllegalArgumentException",
1331 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001332 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001333 }
1334
1335 // Ensure exposure time doesn't overflow (for exposures > 4s)
1336 uint32_t denominator = 1000000000;
1337 while (exposureTime > UINT32_MAX) {
1338 exposureTime >>= 1;
1339 denominator >>= 1;
1340 if (denominator == 0) {
1341 // Should be unreachable
1342 jniThrowException(env, "java/lang/IllegalArgumentException",
1343 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001344 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001345 }
1346 }
1347
1348 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001349 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001350 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001351
1352 }
1353
1354 {
1355 // ISO speed ratings
1356 camera_metadata_entry entry =
1357 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001358 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001359
1360 int32_t tempIso = *(entry.data.i32);
1361 if (tempIso < 0) {
1362 jniThrowException(env, "java/lang/IllegalArgumentException",
1363 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001364 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001365 }
1366
1367 if (tempIso > UINT16_MAX) {
1368 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1369 tempIso = UINT16_MAX;
1370 }
1371
1372 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001373 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001374 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001375 }
1376
1377 {
Emilian Peev7ca13712017-04-05 15:42:22 +01001378 // Baseline exposure
1379 camera_metadata_entry entry =
1380 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
1381 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
1382
1383 // post RAW gain should be boostValue / 100
1384 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
1385 // Baseline exposure should be in EV units so log2(gain) =
1386 // log10(gain)/log10(2)
1387 double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
1388 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
1389 100 };
1390 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
1391 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
1392 }
1393
1394 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001395 // focal length
1396 camera_metadata_entry entry =
1397 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001398 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001399
1400 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001401 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001402 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001403 }
1404
1405 {
1406 // f number
1407 camera_metadata_entry entry =
1408 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001409 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001410
1411 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001412 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001413 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001414 }
1415
1416 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001417 // Set DNG version information
1418 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001419 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001420 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001421
1422 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001423 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1424 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001425 }
1426
1427 {
1428 // Set whitelevel
1429 camera_metadata_entry entry =
1430 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001431 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001432 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001433 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1434 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001435 }
1436
1437 {
1438 // Set default scale
1439 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001440 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1441 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001442 }
1443
1444 bool singleIlluminant = false;
1445 {
1446 // Set calibration illuminants
1447 camera_metadata_entry entry1 =
1448 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001449 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001450 camera_metadata_entry entry2 =
1451 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1452 if (entry2.count == 0) {
1453 singleIlluminant = true;
1454 }
1455 uint16_t ref1 = entry1.data.u8[0];
1456
Ruben Brunk20796122015-07-21 17:51:54 -07001457 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001458 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001459
1460 if (!singleIlluminant) {
1461 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001462 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001463 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001464 }
1465 }
1466
1467 {
1468 // Set color transforms
1469 camera_metadata_entry entry1 =
1470 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001471 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001472
1473 int32_t colorTransform1[entry1.count * 2];
1474
1475 size_t ctr = 0;
1476 for(size_t i = 0; i < entry1.count; ++i) {
1477 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1478 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1479 }
1480
Ruben Brunk20796122015-07-21 17:51:54 -07001481 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1482 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001483
1484 if (!singleIlluminant) {
1485 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001486 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001487 int32_t colorTransform2[entry2.count * 2];
1488
1489 ctr = 0;
1490 for(size_t i = 0; i < entry2.count; ++i) {
1491 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1492 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1493 }
1494
Ruben Brunk20796122015-07-21 17:51:54 -07001495 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1496 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001497 }
1498 }
1499
1500 {
1501 // Set calibration transforms
1502 camera_metadata_entry entry1 =
1503 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001504 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001505
1506 int32_t calibrationTransform1[entry1.count * 2];
1507
1508 size_t ctr = 0;
1509 for(size_t i = 0; i < entry1.count; ++i) {
1510 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1511 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1512 }
1513
Ruben Brunk20796122015-07-21 17:51:54 -07001514 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001515 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001516
1517 if (!singleIlluminant) {
1518 camera_metadata_entry entry2 =
1519 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001520 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001521 int32_t calibrationTransform2[entry2.count * 2];
1522
1523 ctr = 0;
1524 for(size_t i = 0; i < entry2.count; ++i) {
1525 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1526 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1527 }
1528
Ruben Brunk20796122015-07-21 17:51:54 -07001529 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001530 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001531 }
1532 }
1533
1534 {
1535 // Set forward transforms
1536 camera_metadata_entry entry1 =
1537 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001538 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001539
1540 int32_t forwardTransform1[entry1.count * 2];
1541
1542 size_t ctr = 0;
1543 for(size_t i = 0; i < entry1.count; ++i) {
1544 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1545 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1546 }
1547
Ruben Brunk20796122015-07-21 17:51:54 -07001548 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1549 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001550
1551 if (!singleIlluminant) {
1552 camera_metadata_entry entry2 =
1553 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001554 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001555 int32_t forwardTransform2[entry2.count * 2];
1556
1557 ctr = 0;
1558 for(size_t i = 0; i < entry2.count; ++i) {
1559 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1560 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1561 }
1562
Ruben Brunk20796122015-07-21 17:51:54 -07001563 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1564 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001565 }
1566 }
1567
1568 {
1569 // Set camera neutral
1570 camera_metadata_entry entry =
1571 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001572 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001573 uint32_t cameraNeutral[entry.count * 2];
1574
1575 size_t ctr = 0;
1576 for(size_t i = 0; i < entry.count; ++i) {
1577 cameraNeutral[ctr++] =
1578 static_cast<uint32_t>(entry.data.r[i].numerator);
1579 cameraNeutral[ctr++] =
1580 static_cast<uint32_t>(entry.data.r[i].denominator);
1581 }
1582
Ruben Brunk20796122015-07-21 17:51:54 -07001583 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001584 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001585 }
1586
Ruben Brunkf967a542014-04-28 16:31:11 -07001587
1588 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001589 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001590 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001591 return nullptr;
1592 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001593 camera_metadata_entry entry =
1594 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001595 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001596 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1597 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1598 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1599 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001600
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001601 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1602 // relative to the pixel array.
1603 if (imageWidth == width && imageHeight == height) {
1604 xmin = 0;
1605 ymin = 0;
1606 }
1607
Ruben Brunk20796122015-07-21 17:51:54 -07001608 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1609 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1610 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001611 }
1612
1613 {
1614 // Setup unique camera model tag
Tom Cherry93099e42017-10-11 13:44:21 -07001615 std::string model = GetProperty("ro.product.model", "");
1616 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1617 std::string brand = GetProperty("ro.product.brand", "");
Ruben Brunkf967a542014-04-28 16:31:11 -07001618
Tom Cherry93099e42017-10-11 13:44:21 -07001619 String8 cameraModel(model.c_str());
Ruben Brunkf967a542014-04-28 16:31:11 -07001620 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001621 cameraModel += manufacturer.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001622 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001623 cameraModel += brand.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001624
Ruben Brunk20796122015-07-21 17:51:54 -07001625 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001626 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001627 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001628 }
1629
1630 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001631 // Setup sensor noise model
1632 camera_metadata_entry entry =
1633 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1634
Ruben Brunkd70132c2014-08-22 16:24:49 -07001635 const status_t numPlaneColors = 3;
1636 const status_t numCfaChannels = 4;
1637
1638 uint8_t cfaOut[numCfaChannels];
1639 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1640 jniThrowException(env, "java/lang/IllegalArgumentException",
1641 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001642 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001643 }
1644
1645 double noiseProfile[numPlaneColors * 2];
1646
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001647 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001648 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001649 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1650 "in characteristics, no noise profile tag written...",
1651 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001652 } else {
1653 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1654 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1655
Ruben Brunk20796122015-07-21 17:51:54 -07001656 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1657 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1658 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001659 } else {
1660 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1661 " tag written...", __FUNCTION__);
1662 }
1663 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001664 } else {
1665 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1666 __FUNCTION__);
1667 }
1668 }
1669
1670 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001671 // Set up opcode List 2
1672 OpcodeListBuilder builder;
1673 status_t err = OK;
1674
1675 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001676 camera_metadata_entry entry1 =
1677 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001678
1679 uint32_t lsmWidth = 0;
1680 uint32_t lsmHeight = 0;
1681
1682 if (entry1.count != 0) {
1683 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1684 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1685 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001686
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001687 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001688
Ruben Brunk20796122015-07-21 17:51:54 -07001689 camera_metadata_entry entry =
1690 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1691 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1692 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1693 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1694 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1695 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001696 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001697 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001698 err = builder.addGainMapsForMetadata(lsmWidth,
1699 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001700 0,
1701 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001702 height,
1703 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001704 opcodeCfaLayout,
1705 entry2.data.f);
1706 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001707 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1708 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001709 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001710 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001711 }
1712
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001713
1714 // Set up bad pixel correction list
1715 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
1716
1717 if ((entry3.count % 2) != 0) {
1718 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1719 __FUNCTION__);
1720 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1721 return nullptr;
1722 }
1723
1724 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1725 std::vector<uint32_t> v;
Sam Hasinoffc82cd242018-04-11 11:19:41 -07001726 for (size_t i = 0; i < entry3.count; i += 2) {
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001727 int32_t x = entry3.data.i32[i];
1728 int32_t y = entry3.data.i32[i + 1];
1729 x -= static_cast<int32_t>(xmin);
1730 y -= static_cast<int32_t>(ymin);
1731 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
Sam Hasinoffc82cd242018-04-11 11:19:41 -07001732 static_cast<uint32_t>(y) >= height) {
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001733 continue;
1734 }
1735 v.push_back(x);
1736 v.push_back(y);
1737 }
1738 const uint32_t* badPixels = &v[0];
1739 uint32_t badPixelCount = v.size();
1740
1741 if (badPixelCount > 0) {
1742 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1743
1744 if (err != OK) {
1745 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1746 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1747 return nullptr;
1748 }
1749 }
1750
Sam Hasinoff58398342018-08-01 18:24:59 -07001751 if (builder.getCount() > 0) {
1752 size_t listSize = builder.getSize();
1753 uint8_t opcodeListBuf[listSize];
1754 err = builder.buildOpList(opcodeListBuf);
1755 if (err == OK) {
1756 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize,
1757 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1758 } else {
1759 ALOGE("%s: Could not build list of opcodes for lens shading map and bad pixel "
1760 "correction.", __FUNCTION__);
1761 jniThrowRuntimeException(env, "failed to construct opcode list for lens shading "
1762 "map and bad pixel correction");
1763 return nullptr;
1764 }
Ruben Brunkfe816622015-06-16 23:03:09 -07001765 }
1766 }
1767
1768 {
1769 // Set up opcode List 3
1770 OpcodeListBuilder builder;
1771 status_t err = OK;
1772
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001773 // Set up rectilinear distortion correction
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001774 float distortion[6] {1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
1775 bool gotDistortion = false;
1776
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001777 camera_metadata_entry entry4 =
1778 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1779
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001780 if (entry4.count == 5) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001781 float cx = entry4.data.f[/*c_x*/2];
1782 float cy = entry4.data.f[/*c_y*/3];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001783 // Assuming f_x = f_y, or at least close enough.
1784 // Also assuming s = 0, or at least close enough.
1785 float f = entry4.data.f[/*f_x*/0];
1786
1787 camera_metadata_entry entry3 =
1788 results.find(ANDROID_LENS_DISTORTION);
1789 if (entry3.count == 5) {
1790 gotDistortion = true;
Yin-Chia Yeh47849ab2018-06-04 09:23:14 -07001791 float m_x = std::fmaxf(preWidth-1 - cx, cx);
1792 float m_y = std::fmaxf(preHeight-1 - cy, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001793 float m_sq = m_x*m_x + m_y*m_y;
1794 float m = sqrtf(m_sq); // distance to farthest corner from optical center
1795 float f_sq = f * f;
1796 // Conversion factors from Camera2 K factors for new LENS_DISTORTION field
1797 // to DNG spec.
1798 //
1799 // Camera2 / OpenCV assume distortion is applied in a space where focal length
1800 // is factored out, while DNG assumes a normalized space where the distance
1801 // from optical center to the farthest corner is 1.
1802 // Scale from camera2 to DNG spec accordingly.
1803 // distortion[0] is always 1 with the new LENS_DISTORTION field.
1804 const double convCoeff[5] = {
1805 m_sq / f_sq,
1806 pow(m_sq, 2) / pow(f_sq, 2),
1807 pow(m_sq, 3) / pow(f_sq, 3),
1808 m / f,
1809 m / f
1810 };
1811 for (size_t i = 0; i < entry3.count; i++) {
1812 distortion[i+1] = convCoeff[i] * entry3.data.f[i];
1813 }
1814 } else {
1815 entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION);
1816 if (entry3.count == 6) {
1817 gotDistortion = true;
1818 // Conversion factors from Camera2 K factors to DNG spec. K factors:
1819 //
1820 // Note: these are necessary because our unit system assumes a
1821 // normalized max radius of sqrt(2), whereas the DNG spec's
1822 // WarpRectilinear opcode assumes a normalized max radius of 1.
1823 // Thus, each K coefficient must include the domain scaling
1824 // factor (the DNG domain is scaled by sqrt(2) to emulate the
1825 // domain used by the Camera2 specification).
1826 const double convCoeff[6] = {
1827 sqrt(2),
1828 2 * sqrt(2),
1829 4 * sqrt(2),
1830 8 * sqrt(2),
1831 2,
1832 2
1833 };
1834 for (size_t i = 0; i < entry3.count; i++) {
1835 distortion[i] = entry3.data.f[i] * convCoeff[i];
1836 }
1837 }
1838 }
1839 if (gotDistortion) {
1840 err = builder.addWarpRectilinearForMetadata(distortion, preWidth, preHeight, cx,
1841 cy);
1842 if (err != OK) {
1843 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1844 jniThrowRuntimeException(env, "failed to add distortion correction.");
1845 return nullptr;
1846 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001847 }
1848 }
1849
Sam Hasinoff58398342018-08-01 18:24:59 -07001850 if (builder.getCount() > 0) {
1851 size_t listSize = builder.getSize();
1852 uint8_t opcodeListBuf[listSize];
1853 err = builder.buildOpList(opcodeListBuf);
1854 if (err == OK) {
1855 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize,
1856 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
1857 } else {
1858 ALOGE("%s: Could not build list of opcodes for distortion correction.",
1859 __FUNCTION__);
1860 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1861 " correction");
1862 return nullptr;
1863 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001864 }
1865 }
1866
Ruben Brunk20796122015-07-21 17:51:54 -07001867 {
1868 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08001869 // Note: There's only one orientation field for the whole file, in IFD0
1870 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07001871 uint16_t orientation = nativeContext->getOrientation();
1872 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1873 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001874
Ruben Brunk47e91f22014-05-28 18:38:42 -07001875 }
1876
Ruben Brunk20796122015-07-21 17:51:54 -07001877 if (nativeContext->hasDescription()){
1878 // Set Description
1879 String8 description = nativeContext->getDescription();
1880 size_t len = description.bytes() + 1;
1881 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1882 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1883 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1884 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001885 }
1886 }
1887
Ruben Brunk20796122015-07-21 17:51:54 -07001888 if (nativeContext->hasGpsData()) {
1889 // Set GPS tags
1890 GpsData gpsData = nativeContext->getGpsData();
1891 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1892 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1893 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1894 TIFF_IFD_0);
1895 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1896 return nullptr;
1897 }
1898 }
1899
1900 {
1901 uint8_t version[] = {2, 3, 0, 0};
1902 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1903 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1904 }
1905
1906 {
1907 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1908 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1909 TAG_GPSLATITUDEREF, writer);
1910 }
1911
1912 {
1913 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1914 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1915 TAG_GPSLONGITUDEREF, writer);
1916 }
1917
1918 {
1919 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1920 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1921 }
1922
1923 {
1924 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1925 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1926 }
1927
1928 {
1929 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1930 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1931 }
1932
1933 {
1934 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1935 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1936 TAG_GPSDATESTAMP, writer);
1937 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001938 }
1939
Ruben Brunk47e91f22014-05-28 18:38:42 -07001940
Ruben Brunk20796122015-07-21 17:51:54 -07001941 if (nativeContext->hasThumbnail()) {
1942 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1943 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1944 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1945 TIFF_IFD_0);
1946 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1947 return nullptr;
1948 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001949 }
1950
1951 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001952 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001953 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001954 tagsToMove.add(TAG_BITSPERSAMPLE);
1955 tagsToMove.add(TAG_COMPRESSION);
1956 tagsToMove.add(TAG_IMAGEWIDTH);
1957 tagsToMove.add(TAG_IMAGELENGTH);
1958 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1959 tagsToMove.add(TAG_BLACKLEVEL);
1960 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1961 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1962 tagsToMove.add(TAG_PLANARCONFIGURATION);
1963 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1964 tagsToMove.add(TAG_CFAPATTERN);
1965 tagsToMove.add(TAG_CFAPLANECOLOR);
1966 tagsToMove.add(TAG_CFALAYOUT);
1967 tagsToMove.add(TAG_XRESOLUTION);
1968 tagsToMove.add(TAG_YRESOLUTION);
1969 tagsToMove.add(TAG_RESOLUTIONUNIT);
1970 tagsToMove.add(TAG_WHITELEVEL);
1971 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001972 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1973 tagsToMove.add(TAG_DEFAULTCROPSIZE);
Emilian Peeva7503b92018-08-13 12:46:05 +01001974
1975 if (nullptr != writer->getEntry(TAG_OPCODELIST2, TIFF_IFD_0).get()) {
1976 tagsToMove.add(TAG_OPCODELIST2);
1977 }
1978
1979 if (nullptr != writer->getEntry(TAG_OPCODELIST3, TIFF_IFD_0).get()) {
1980 tagsToMove.add(TAG_OPCODELIST3);
1981 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001982
1983 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1984 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07001985 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001986 }
1987
Ruben Brunk20796122015-07-21 17:51:54 -07001988 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001989
Ruben Brunk20796122015-07-21 17:51:54 -07001990 {
1991 // Set photometric interpretation
1992 uint16_t interpretation = 2; // RGB
1993 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1994 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001995 }
Ruben Brunk20796122015-07-21 17:51:54 -07001996
1997 {
1998 // Set planar configuration
1999 uint16_t config = 1; // Chunky
2000 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
2001 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
2002 }
2003
2004 {
2005 // Set samples per pixel
2006 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
2007 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
2008 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
2009 }
2010
2011 {
2012 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08002013 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
2014 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
2015 BAIL_IF_INVALID_RET_NULL_SP(
2016 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07002017 env, TAG_BITSPERSAMPLE, writer);
2018 }
2019
2020 {
2021 // Set subfiletype
2022 uint32_t subfileType = 1; // Thumbnail image
2023 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
2024 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
2025 }
2026
2027 {
2028 // Set compression
2029 uint16_t compression = 1; // None
2030 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
2031 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
2032 }
2033
2034 {
2035 // Set dimensions
2036 uint32_t uWidth = nativeContext->getThumbnailWidth();
2037 uint32_t uHeight = nativeContext->getThumbnailHeight();
2038 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
2039 env, TAG_IMAGEWIDTH, writer);
2040 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
2041 env, TAG_IMAGELENGTH, writer);
2042 }
2043
2044 {
2045 // x resolution
2046 uint32_t xres[] = { 72, 1 }; // default 72 ppi
2047 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
2048 env, TAG_XRESOLUTION, writer);
2049
2050 // y resolution
2051 uint32_t yres[] = { 72, 1 }; // default 72 ppi
2052 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
2053 env, TAG_YRESOLUTION, writer);
2054
2055 uint16_t unit = 2; // inches
2056 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
2057 env, TAG_RESOLUTIONUNIT, writer);
2058 }
2059 }
2060
2061 if (writer->addStrip(TIFF_IFD_0) != OK) {
2062 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
2063 jniThrowException(env, "java/lang/IllegalStateException",
2064 "Failed to setup thumbnail strip tags.");
2065 return nullptr;
2066 }
2067
2068 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002069 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
2070 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
2071 jniThrowException(env, "java/lang/IllegalStateException",
2072 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07002073 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002074 }
2075 }
Ruben Brunk20796122015-07-21 17:51:54 -07002076 return writer;
2077}
2078
2079static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2080 ALOGV("%s:", __FUNCTION__);
2081 DngCreator_setNativeContext(env, thiz, nullptr);
2082}
2083
2084static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2085 ALOGV("%s:", __FUNCTION__);
2086
2087 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2088 if (context == nullptr) {
2089 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2090 jniThrowException(env, "java/lang/AssertionError",
2091 "setOrientation called with uninitialized DngCreator");
2092 return;
2093 }
2094
2095 uint16_t orientation = static_cast<uint16_t>(orient);
2096 context->setOrientation(orientation);
2097}
2098
2099static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2100 ALOGV("%s:", __FUNCTION__);
2101
2102 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2103 if (context == nullptr) {
2104 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2105 jniThrowException(env, "java/lang/AssertionError",
2106 "setDescription called with uninitialized DngCreator");
2107 return;
2108 }
2109
2110 const char* desc = env->GetStringUTFChars(description, nullptr);
2111 context->setDescription(String8(desc));
2112 env->ReleaseStringUTFChars(description, desc);
2113}
2114
2115static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2116 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2117 ALOGV("%s:", __FUNCTION__);
2118
2119 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2120 if (context == nullptr) {
2121 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2122 jniThrowException(env, "java/lang/AssertionError",
2123 "setGpsTags called with uninitialized DngCreator");
2124 return;
2125 }
2126
2127 GpsData data;
2128
2129 jsize latLen = env->GetArrayLength(latTag);
2130 jsize longLen = env->GetArrayLength(longTag);
2131 jsize timeLen = env->GetArrayLength(timeTag);
2132 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2133 jniThrowException(env, "java/lang/IllegalArgumentException",
2134 "invalid latitude tag length");
2135 return;
2136 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2137 jniThrowException(env, "java/lang/IllegalArgumentException",
2138 "invalid longitude tag length");
2139 return;
2140 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2141 jniThrowException(env, "java/lang/IllegalArgumentException",
2142 "invalid time tag length");
2143 return;
2144 }
2145
2146 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2147 reinterpret_cast<jint*>(&data.mLatitude));
2148 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2149 reinterpret_cast<jint*>(&data.mLongitude));
2150 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2151 reinterpret_cast<jint*>(&data.mTimestamp));
2152
2153
2154 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2155 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2156 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2157 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2158 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2159 reinterpret_cast<char*>(&data.mDate));
2160 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2161
2162 context->setGpsData(data);
2163}
2164
2165static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2166 jint height) {
2167 ALOGV("%s:", __FUNCTION__);
2168
2169 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2170 if (context == nullptr) {
2171 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2172 jniThrowException(env, "java/lang/AssertionError",
2173 "setThumbnail called with uninitialized DngCreator");
2174 return;
2175 }
2176
2177 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2178 jlong capacity = env->GetDirectBufferCapacity(buffer);
2179 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2180 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2181 "Invalid size %d for thumbnail, expected size was %d",
2182 capacity, fullSize);
2183 return;
2184 }
2185
2186 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2187 if (pixelBytes == nullptr) {
2188 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2189 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2190 return;
2191 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002192
2193 if (!context->setThumbnail(pixelBytes, width, height)) {
2194 jniThrowException(env, "java/lang/IllegalStateException",
2195 "Failed to set thumbnail.");
2196 return;
2197 }
2198}
2199
2200// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002201static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002202 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2203 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002204 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002205 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2206 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2207 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002208 uint32_t rStride = static_cast<uint32_t>(rowStride);
2209 uint32_t pStride = static_cast<uint32_t>(pixStride);
2210 uint32_t uWidth = static_cast<uint32_t>(width);
2211 uint32_t uHeight = static_cast<uint32_t>(height);
2212 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002213
2214 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2215 if(env->ExceptionCheck()) {
2216 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2217 return;
2218 }
2219
Ruben Brunk47e91f22014-05-28 18:38:42 -07002220 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002221 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002222 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2223 jniThrowException(env, "java/lang/AssertionError",
2224 "Write called with uninitialized DngCreator");
2225 return;
2226 }
Ruben Brunk20796122015-07-21 17:51:54 -07002227 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002228
Ruben Brunk20796122015-07-21 17:51:54 -07002229 if (writer.get() == nullptr) {
2230 return;
2231 }
2232
2233 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002234 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002235 return;
2236 }
2237
Ruben Brunk47e91f22014-05-28 18:38:42 -07002238 sp<JniInputByteBuffer> inBuf;
2239 Vector<StripSource*> sources;
2240 sp<DirectStripSource> thumbnailSource;
2241 uint32_t targetIfd = TIFF_IFD_0;
2242
2243 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2244
2245 if (hasThumbnail) {
2246 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2247 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2248 uint32_t thumbWidth = context->getThumbnailWidth();
2249 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2250 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2251 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2252 SAMPLES_PER_RGB_PIXEL);
2253 sources.add(thumbnailSource.get());
2254 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002255 }
2256
Ruben Brunk47e91f22014-05-28 18:38:42 -07002257 if (isDirect) {
2258 size_t fullSize = rStride * uHeight;
2259 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2260 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2261 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2262 "Invalid size %d for Image, size given in metadata is %d at current stride",
2263 capacity, fullSize);
2264 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002265 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002266
Ruben Brunk47e91f22014-05-28 18:38:42 -07002267 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002268 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002269 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2270 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2271 return;
2272 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002273
Ruben Brunk47e91f22014-05-28 18:38:42 -07002274 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2275 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2276 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2277 sources.add(&stripSource);
2278
2279 status_t ret = OK;
2280 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2281 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002282 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002283 jniThrowExceptionFmt(env, "java/io/IOException",
2284 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002285 }
2286 return;
2287 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002288 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002289 inBuf = new JniInputByteBuffer(env, inBuffer);
2290
2291 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2292 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2293 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2294 sources.add(&stripSource);
2295
2296 status_t ret = OK;
2297 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2298 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2299 if (!env->ExceptionCheck()) {
2300 jniThrowExceptionFmt(env, "java/io/IOException",
2301 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002302 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002303 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002304 }
2305 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002306}
2307
2308static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002309 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002310 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002311
2312 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2313 uint32_t pixStride = BYTES_PER_SAMPLE;
2314 uint32_t uWidth = static_cast<uint32_t>(width);
2315 uint32_t uHeight = static_cast<uint32_t>(height);
2316 uint64_t uOffset = static_cast<uint32_t>(offset);
2317
Dan Albert46d84442014-11-18 16:07:51 -08002318 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2319 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2320 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002321
2322 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002323 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002324 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2325 return;
2326 }
2327
Ruben Brunk47e91f22014-05-28 18:38:42 -07002328 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002329 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002330 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2331 jniThrowException(env, "java/lang/AssertionError",
2332 "Write called with uninitialized DngCreator");
2333 return;
2334 }
Ruben Brunk20796122015-07-21 17:51:54 -07002335 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002336
Ruben Brunk20796122015-07-21 17:51:54 -07002337 if (writer.get() == nullptr) {
2338 return;
2339 }
2340
2341 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002342 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002343 return;
2344 }
2345
2346 sp<DirectStripSource> thumbnailSource;
2347 uint32_t targetIfd = TIFF_IFD_0;
2348 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2349 Vector<StripSource*> sources;
2350
2351 if (hasThumbnail) {
2352 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2353 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2354 uint32_t width = context->getThumbnailWidth();
2355 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2356 width, context->getThumbnailHeight(), bytesPerPixel,
2357 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2358 SAMPLES_PER_RGB_PIXEL);
2359 sources.add(thumbnailSource.get());
2360 targetIfd = TIFF_IFD_SUB1;
2361 }
2362
2363 sp<JniInputStream> in = new JniInputStream(env, inStream);
2364
2365 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2366 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2367 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2368 sources.add(&stripSource);
2369
2370 status_t ret = OK;
2371 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2372 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2373 if (!env->ExceptionCheck()) {
2374 jniThrowExceptionFmt(env, "java/io/IOException",
2375 "Encountered error %d while writing file.", ret);
2376 }
2377 return;
2378 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002379}
2380
2381} /*extern "C" */
2382
Daniel Micay76f6a862015-09-19 17:31:01 -04002383static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002384 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2385 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002386 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2387 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002388 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2389 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002390 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2391 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2392 (void*) DngCreator_nativeSetGpsTags},
2393 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2394 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002395 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002396 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002397 (void*) DngCreator_nativeWriteInputStream},
2398};
2399
Ruben Brunkb6079002014-05-22 12:33:54 -07002400int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002401 return RegisterMethodsOrDie(env,
2402 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002403}