blob: 1c247cbb7743ddc59bee29c7960d583715a22390 [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>
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -070022#include <array>
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070023#include <memory>
Ruben Brunk9ce22a02015-08-03 12:40:11 -070024#include <vector>
Emilian Peev7ca13712017-04-05 15:42:22 +010025#include <cmath>
Dan Albert46d84442014-11-18 16:07:51 -080026
Tom Cherry93099e42017-10-11 13:44:21 -070027#include <android-base/properties.h>
Dan Albert46d84442014-11-18 16:07:51 -080028#include <utils/Log.h>
29#include <utils/Errors.h>
30#include <utils/StrongPointer.h>
31#include <utils/RefBase.h>
32#include <utils/Vector.h>
Ruben Brunk20796122015-07-21 17:51:54 -070033#include <utils/String8.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070034#include <system/camera_metadata.h>
35#include <camera/CameraMetadata.h>
36#include <img_utils/DngUtils.h>
37#include <img_utils/TagDefinitions.h>
38#include <img_utils/TiffIfd.h>
39#include <img_utils/TiffWriter.h>
40#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070041#include <img_utils/Input.h>
42#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070043
Andreas Gampeed6b9df2014-11-20 22:02:20 -080044#include "core_jni_helpers.h"
Ruben Brunkb8df8e02014-06-02 22:59:45 -070045
Ruben Brunkf967a542014-04-28 16:31:11 -070046#include "android_runtime/AndroidRuntime.h"
47#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
48
49#include <jni.h>
Steven Moreland2279b252017-07-19 09:50:45 -070050#include <nativehelper/JNIHelp.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070051
52using namespace android;
53using namespace img_utils;
Tom Cherry93099e42017-10-11 13:44:21 -070054using android::base::GetProperty;
Ruben Brunkf967a542014-04-28 16:31:11 -070055
Ruben Brunk20796122015-07-21 17:51:54 -070056#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070057 if ((expr) != OK) { \
58 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070059 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070060 return false; \
Ruben Brunkf967a542014-04-28 16:31:11 -070061 }
62
Ruben Brunk20796122015-07-21 17:51:54 -070063
64#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
65 if ((expr) != OK) { \
66 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
67 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
68 return nullptr; \
69 }
70
71
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070072#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
73 if ((expr) != OK) { \
74 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
75 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
76 return -1; \
77 }
78
Ruben Brunk20796122015-07-21 17:51:54 -070079#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
Chih-Hung Hsieh3c22e002016-05-19 15:10:07 -070080 if ((entry).count == 0) { \
Ruben Brunkf967a542014-04-28 16:31:11 -070081 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070082 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070083 return nullptr; \
Ruben Brunkf967a542014-04-28 16:31:11 -070084 }
85
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -080086#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
87 if (expr) { \
88 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
89 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
90 return nullptr; \
91 }
92
Ruben Brunk20796122015-07-21 17:51:54 -070093
Ruben Brunkb6079002014-05-22 12:33:54 -070094#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070095
96static struct {
97 jfieldID mNativeContext;
98} gDngCreatorClassInfo;
99
100static struct {
101 jmethodID mWriteMethod;
102} gOutputStreamClassInfo;
103
Ruben Brunk47e91f22014-05-28 18:38:42 -0700104static struct {
105 jmethodID mReadMethod;
106 jmethodID mSkipMethod;
107} gInputStreamClassInfo;
108
109static struct {
110 jmethodID mGetMethod;
111} gInputByteBufferClassInfo;
112
Ruben Brunkf967a542014-04-28 16:31:11 -0700113enum {
114 BITS_PER_SAMPLE = 16,
115 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700116 BYTES_PER_RGB_PIXEL = 3,
117 BITS_PER_RGB_SAMPLE = 8,
118 BYTES_PER_RGB_SAMPLE = 1,
119 SAMPLES_PER_RGB_PIXEL = 3,
120 SAMPLES_PER_RAW_PIXEL = 1,
121 TIFF_IFD_0 = 0,
122 TIFF_IFD_SUB1 = 1,
123 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700124};
125
Ruben Brunk20796122015-07-21 17:51:54 -0700126
127/**
128 * POD container class for GPS tag data.
129 */
130class GpsData {
131public:
132 enum {
133 GPS_VALUE_LENGTH = 6,
134 GPS_REF_LENGTH = 2,
135 GPS_DATE_LENGTH = 11,
136 };
137
138 uint32_t mLatitude[GPS_VALUE_LENGTH];
139 uint32_t mLongitude[GPS_VALUE_LENGTH];
140 uint32_t mTimestamp[GPS_VALUE_LENGTH];
141 uint8_t mLatitudeRef[GPS_REF_LENGTH];
142 uint8_t mLongitudeRef[GPS_REF_LENGTH];
143 uint8_t mDate[GPS_DATE_LENGTH];
144};
145
Ruben Brunkf967a542014-04-28 16:31:11 -0700146// ----------------------------------------------------------------------------
147
Ruben Brunk47e91f22014-05-28 18:38:42 -0700148/**
149 * Container class for the persistent native context.
150 */
151
152class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700153public:
Ruben Brunk20796122015-07-21 17:51:54 -0700154 enum {
155 DATETIME_COUNT = 20,
156 };
157
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700158 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700159 virtual ~NativeContext();
160
161 TiffWriter* getWriter();
162
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700163 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
164 std::shared_ptr<const CameraMetadata> getResult() const;
165
Ruben Brunk20796122015-07-21 17:51:54 -0700166 uint32_t getThumbnailWidth() const;
167 uint32_t getThumbnailHeight() const;
168 const uint8_t* getThumbnail() const;
169 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700170
171 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
172
Ruben Brunk20796122015-07-21 17:51:54 -0700173 void setOrientation(uint16_t orientation);
174 uint16_t getOrientation() const;
175
176 void setDescription(const String8& desc);
177 String8 getDescription() const;
178 bool hasDescription() const;
179
180 void setGpsData(const GpsData& data);
181 GpsData getGpsData() const;
182 bool hasGpsData() const;
183
184 void setCaptureTime(const String8& formattedCaptureTime);
185 String8 getCaptureTime() const;
186 bool hasCaptureTime() const;
187
Ruben Brunk47e91f22014-05-28 18:38:42 -0700188private:
189 Vector<uint8_t> mCurrentThumbnail;
190 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700191 std::shared_ptr<CameraMetadata> mCharacteristics;
192 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700193 uint32_t mThumbnailWidth;
194 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700195 uint16_t mOrientation;
196 bool mThumbnailSet;
197 bool mGpsSet;
198 bool mDescriptionSet;
199 bool mCaptureTimeSet;
200 String8 mDescription;
201 GpsData mGpsData;
202 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700203};
204
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700205NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
206 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
207 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -0800208 mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
209 mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700210
211NativeContext::~NativeContext() {}
212
213TiffWriter* NativeContext::getWriter() {
214 return &mWriter;
215}
216
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700217std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
218 return mCharacteristics;
219}
220
221std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
222 return mResult;
223}
224
Ruben Brunk20796122015-07-21 17:51:54 -0700225uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700226 return mThumbnailWidth;
227}
228
Ruben Brunk20796122015-07-21 17:51:54 -0700229uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700230 return mThumbnailHeight;
231}
232
Ruben Brunk20796122015-07-21 17:51:54 -0700233const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700234 return mCurrentThumbnail.array();
235}
236
Ruben Brunk20796122015-07-21 17:51:54 -0700237bool NativeContext::hasThumbnail() const {
238 return mThumbnailSet;
239}
240
Ruben Brunk47e91f22014-05-28 18:38:42 -0700241bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
242 mThumbnailWidth = width;
243 mThumbnailHeight = height;
244
245 size_t size = BYTES_PER_RGB_PIXEL * width * height;
246 if (mCurrentThumbnail.resize(size) < 0) {
247 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
248 return false;
249 }
250
251 uint8_t* thumb = mCurrentThumbnail.editArray();
252 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700253 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700254 return true;
255}
256
Ruben Brunk20796122015-07-21 17:51:54 -0700257void NativeContext::setOrientation(uint16_t orientation) {
258 mOrientation = orientation;
259}
260
261uint16_t NativeContext::getOrientation() const {
262 return mOrientation;
263}
264
265void NativeContext::setDescription(const String8& desc) {
266 mDescription = desc;
267 mDescriptionSet = true;
268}
269
270String8 NativeContext::getDescription() const {
271 return mDescription;
272}
273
274bool NativeContext::hasDescription() const {
275 return mDescriptionSet;
276}
277
278void NativeContext::setGpsData(const GpsData& data) {
279 mGpsData = data;
280 mGpsSet = true;
281}
282
283GpsData NativeContext::getGpsData() const {
284 return mGpsData;
285}
286
287bool NativeContext::hasGpsData() const {
288 return mGpsSet;
289}
290
291void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
292 mFormattedCaptureTime = formattedCaptureTime;
293 mCaptureTimeSet = true;
294}
295
296String8 NativeContext::getCaptureTime() const {
297 return mFormattedCaptureTime;
298}
299
300bool NativeContext::hasCaptureTime() const {
301 return mCaptureTimeSet;
302}
303
Ruben Brunk47e91f22014-05-28 18:38:42 -0700304// End of NativeContext
305// ----------------------------------------------------------------------------
306
307/**
308 * Wrapper class for a Java OutputStream.
309 *
310 * This class is not intended to be used across JNI calls.
311 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700312class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
313public:
314 JniOutputStream(JNIEnv* env, jobject outStream);
315
316 virtual ~JniOutputStream();
317
318 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700319
Ruben Brunkf967a542014-04-28 16:31:11 -0700320 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700321
Ruben Brunkf967a542014-04-28 16:31:11 -0700322 status_t close();
323private:
324 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700325 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700326 };
327 jobject mOutputStream;
328 JNIEnv* mEnv;
329 jbyteArray mByteArray;
330};
331
332JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
333 mEnv(env) {
334 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700335 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700336 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
337 }
338}
339
340JniOutputStream::~JniOutputStream() {
341 mEnv->DeleteLocalRef(mByteArray);
342}
343
344status_t JniOutputStream::open() {
345 // Do nothing
346 return OK;
347}
348
349status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
350 while(count > 0) {
351 size_t len = BYTE_ARRAY_LENGTH;
352 len = (count > len) ? len : count;
353 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
354
355 if (mEnv->ExceptionCheck()) {
356 return BAD_VALUE;
357 }
358
359 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
360 0, len);
361
362 if (mEnv->ExceptionCheck()) {
363 return BAD_VALUE;
364 }
365
366 count -= len;
367 offset += len;
368 }
369 return OK;
370}
371
372status_t JniOutputStream::close() {
373 // Do nothing
374 return OK;
375}
376
Ruben Brunk47e91f22014-05-28 18:38:42 -0700377// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700378// ----------------------------------------------------------------------------
379
Ruben Brunk47e91f22014-05-28 18:38:42 -0700380/**
381 * Wrapper class for a Java InputStream.
382 *
383 * This class is not intended to be used across JNI calls.
384 */
385class JniInputStream : public Input, public LightRefBase<JniInputStream> {
386public:
387 JniInputStream(JNIEnv* env, jobject inStream);
388
389 status_t open();
390
391 status_t close();
392
393 ssize_t read(uint8_t* buf, size_t offset, size_t count);
394
395 ssize_t skip(size_t count);
396
397 virtual ~JniInputStream();
398private:
399 enum {
400 BYTE_ARRAY_LENGTH = 4096
401 };
402 jobject mInStream;
403 JNIEnv* mEnv;
404 jbyteArray mByteArray;
405
406};
407
408JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
409 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700410 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700411 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
412 }
413}
414
415JniInputStream::~JniInputStream() {
416 mEnv->DeleteLocalRef(mByteArray);
417}
418
419ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
420
421 jint realCount = BYTE_ARRAY_LENGTH;
422 if (count < BYTE_ARRAY_LENGTH) {
423 realCount = count;
424 }
425 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
426 realCount);
427
428 if (actual < 0) {
429 return NOT_ENOUGH_DATA;
430 }
431
432 if (mEnv->ExceptionCheck()) {
433 return BAD_VALUE;
434 }
435
436 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
437 if (mEnv->ExceptionCheck()) {
438 return BAD_VALUE;
439 }
440 return actual;
441}
442
443ssize_t JniInputStream::skip(size_t count) {
444 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
445 static_cast<jlong>(count));
446
447 if (mEnv->ExceptionCheck()) {
448 return BAD_VALUE;
449 }
450 if (actual < 0) {
451 return NOT_ENOUGH_DATA;
452 }
453 return actual;
454}
455
456status_t JniInputStream::open() {
457 // Do nothing
458 return OK;
459}
460
461status_t JniInputStream::close() {
462 // Do nothing
463 return OK;
464}
465
466// End of JniInputStream
467// ----------------------------------------------------------------------------
468
469/**
470 * Wrapper class for a non-direct Java ByteBuffer.
471 *
472 * This class is not intended to be used across JNI calls.
473 */
474class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
475public:
476 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
477
478 status_t open();
479
480 status_t close();
481
482 ssize_t read(uint8_t* buf, size_t offset, size_t count);
483
484 virtual ~JniInputByteBuffer();
485private:
486 enum {
487 BYTE_ARRAY_LENGTH = 4096
488 };
489 jobject mInBuf;
490 JNIEnv* mEnv;
491 jbyteArray mByteArray;
492};
493
494JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
495 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700496 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700497 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
498 }
499}
500
501JniInputByteBuffer::~JniInputByteBuffer() {
502 mEnv->DeleteLocalRef(mByteArray);
503}
504
505ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
506 jint realCount = BYTE_ARRAY_LENGTH;
507 if (count < BYTE_ARRAY_LENGTH) {
508 realCount = count;
509 }
510
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700511 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
512 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800513 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700514
515 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800516 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700517 return BAD_VALUE;
518 }
519
520 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
521 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800522 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700523 return BAD_VALUE;
524 }
525 return realCount;
526}
527
528status_t JniInputByteBuffer::open() {
529 // Do nothing
530 return OK;
531}
532
533status_t JniInputByteBuffer::close() {
534 // Do nothing
535 return OK;
536}
537
538// End of JniInputByteBuffer
539// ----------------------------------------------------------------------------
540
541/**
542 * StripSource subclass for Input types.
543 *
544 * This class is not intended to be used across JNI calls.
545 */
546
547class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
548public:
549 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
550 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
551 uint32_t samplesPerPixel);
552
553 virtual ~InputStripSource();
554
555 virtual status_t writeToStream(Output& stream, uint32_t count);
556
557 virtual uint32_t getIfd() const;
558protected:
559 uint32_t mIfd;
560 Input* mInput;
561 uint32_t mWidth;
562 uint32_t mHeight;
563 uint32_t mPixStride;
564 uint32_t mRowStride;
565 uint64_t mOffset;
566 JNIEnv* mEnv;
567 uint32_t mBytesPerSample;
568 uint32_t mSamplesPerPixel;
569};
570
571InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
572 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
573 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
574 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
575 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
576 mSamplesPerPixel(samplesPerPixel) {}
577
578InputStripSource::~InputStripSource() {}
579
580status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700581 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700582 jlong offset = mOffset;
583
584 if (fullSize != count) {
585 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
586 fullSize);
587 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
588 return BAD_VALUE;
589 }
590
591 // Skip offset
592 while (offset > 0) {
593 ssize_t skipped = mInput->skip(offset);
594 if (skipped <= 0) {
595 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
596 jniThrowExceptionFmt(mEnv, "java/io/IOException",
597 "Early EOF encountered in skip, not enough pixel data for image of size %u",
598 fullSize);
599 skipped = NOT_ENOUGH_DATA;
600 } else {
601 if (!mEnv->ExceptionCheck()) {
602 jniThrowException(mEnv, "java/io/IOException",
603 "Error encountered while skip bytes in input stream.");
604 }
605 }
606
607 return skipped;
608 }
609 offset -= skipped;
610 }
611
612 Vector<uint8_t> row;
613 if (row.resize(mRowStride) < 0) {
614 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
615 return BAD_VALUE;
616 }
617
618 uint8_t* rowBytes = row.editArray();
619
620 for (uint32_t i = 0; i < mHeight; ++i) {
621 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800622 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700623
624 while (rowFillAmt < mRowStride) {
625 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
626 if (bytesRead <= 0) {
627 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800628 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
629 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700630 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800631 "Early EOF encountered, not enough pixel data for image of size %"
632 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700633 bytesRead = NOT_ENOUGH_DATA;
634 } else {
635 if (!mEnv->ExceptionCheck()) {
636 jniThrowException(mEnv, "java/io/IOException",
637 "Error encountered while reading");
638 }
639 }
640 return bytesRead;
641 }
642 rowFillAmt += bytesRead;
643 rowSize -= bytesRead;
644 }
645
646 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
647 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
648
649 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
650 mEnv->ExceptionCheck()) {
651 if (!mEnv->ExceptionCheck()) {
652 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
653 }
654 return BAD_VALUE;
655 }
656 } else {
657 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
658 jniThrowException(mEnv, "java/lang/IllegalStateException",
659 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
660 return BAD_VALUE;
661
662 // TODO: Add support for non-contiguous pixels if needed.
663 }
664 }
665 return OK;
666}
667
668uint32_t InputStripSource::getIfd() const {
669 return mIfd;
670}
671
672// End of InputStripSource
673// ----------------------------------------------------------------------------
674
675/**
676 * StripSource subclass for direct buffer types.
677 *
678 * This class is not intended to be used across JNI calls.
679 */
680
681class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
682public:
683 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
684 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
685 uint32_t bytesPerSample, uint32_t samplesPerPixel);
686
687 virtual ~DirectStripSource();
688
689 virtual status_t writeToStream(Output& stream, uint32_t count);
690
691 virtual uint32_t getIfd() const;
692protected:
693 uint32_t mIfd;
694 const uint8_t* mPixelBytes;
695 uint32_t mWidth;
696 uint32_t mHeight;
697 uint32_t mPixStride;
698 uint32_t mRowStride;
699 uint16_t mOffset;
700 JNIEnv* mEnv;
701 uint32_t mBytesPerSample;
702 uint32_t mSamplesPerPixel;
703};
704
705DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
706 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
707 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
708 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
709 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
710 mSamplesPerPixel(samplesPerPixel) {}
711
712DirectStripSource::~DirectStripSource() {}
713
714status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700715 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700716
717 if (fullSize != count) {
718 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
719 fullSize);
720 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
721 return BAD_VALUE;
722 }
723
Ruben Brunk20796122015-07-21 17:51:54 -0700724
Ruben Brunk47e91f22014-05-28 18:38:42 -0700725 if (mPixStride == mBytesPerSample * mSamplesPerPixel
726 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
727 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
728
729 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
730 if (!mEnv->ExceptionCheck()) {
731 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
732 }
733 return BAD_VALUE;
734 }
735 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
736 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
737
738 for (size_t i = 0; i < mHeight; ++i) {
739 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
740 mEnv->ExceptionCheck()) {
741 if (!mEnv->ExceptionCheck()) {
742 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
743 }
744 return BAD_VALUE;
745 }
746 }
747 } else {
748 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
749
750 jniThrowException(mEnv, "java/lang/IllegalStateException",
751 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
752 return BAD_VALUE;
753
754 // TODO: Add support for non-contiguous pixels if needed.
755 }
756 return OK;
757
758}
759
760uint32_t DirectStripSource::getIfd() const {
761 return mIfd;
762}
763
764// End of DirectStripSource
765// ----------------------------------------------------------------------------
766
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700767/**
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700768 * Calculate the default crop relative to the "active area" of the image sensor (this active area
769 * will always be the pre-correction active area rectangle), and set this.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700770 */
771static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700772 sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700773
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700774 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700775 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700776 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
777 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
778
Ruben Brunk20796122015-07-21 17:51:54 -0700779 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
780
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700781 if (width < margin * 2 || height < margin * 2) {
782 ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too"
783 "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width);
784 jniThrowException(env, "java/lang/IllegalStateException",
785 "Pre-correction active area is too small.");
786 return BAD_VALUE;
Ruben Brunk20796122015-07-21 17:51:54 -0700787 }
788
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700789 uint32_t defaultCropOrigin[] = {margin, margin};
790 uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin,
791 height - defaultCropOrigin[1] - margin};
792
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700793 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
794 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
795 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
796 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
797
798 return OK;
799}
800
Ruben Brunk20796122015-07-21 17:51:54 -0700801static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700802 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700803 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700804 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700805 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700806 return false;
807 }
808
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700809 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700810 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700811 "Image height %d is invalid", height);
812 return false;
813 }
814
815 camera_metadata_ro_entry preCorrectionEntry =
816 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
817 camera_metadata_ro_entry pixelArrayEntry =
818 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
819
820 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
821 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
822 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
823 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
824
825 bool matchesPixelArray = (pWidth == width && pHeight == height);
826 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
827
Ruben Brunk20796122015-07-21 17:51:54 -0700828 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700829 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
830 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
831 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
832 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700833 return false;
834 }
835
836 return true;
837}
838
Ruben Brunk20796122015-07-21 17:51:54 -0700839static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700840 const Vector<uint16_t>& entries) {
841 for (size_t i = 0; i < entries.size(); ++i) {
842 uint16_t tagId = entries[i];
843 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700844 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700845 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
846 ifdFrom);
847 return BAD_VALUE;
848 }
849 if (writer->addEntry(entry, ifdTo) != OK) {
850 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
851 ifdFrom);
852 return BAD_VALUE;
853 }
854 writer->removeEntry(tagId, ifdFrom);
855 }
856 return OK;
857}
858
Ruben Brunkd70132c2014-08-22 16:24:49 -0700859/**
860 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
861 * Returns OK on success, or a negative error code if the CFA enum was invalid.
862 */
863static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
864 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
865 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
866 cfaEnum);
867 switch(cfa) {
868 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
869 cfaOut[0] = 0;
870 cfaOut[1] = 1;
871 cfaOut[2] = 1;
872 cfaOut[3] = 2;
873 break;
874 }
875 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
876 cfaOut[0] = 1;
877 cfaOut[1] = 0;
878 cfaOut[2] = 2;
879 cfaOut[3] = 1;
880 break;
881 }
882 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
883 cfaOut[0] = 1;
884 cfaOut[1] = 2;
885 cfaOut[2] = 0;
886 cfaOut[3] = 1;
887 break;
888 }
889 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
890 cfaOut[0] = 2;
891 cfaOut[1] = 1;
892 cfaOut[2] = 1;
893 cfaOut[3] = 0;
894 break;
895 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -0700896 // MONO and NIR are degenerate case of RGGB pattern: only Red channel
897 // will be used.
898 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO:
899 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR: {
900 cfaOut[0] = 0;
901 break;
902 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700903 default: {
904 return BAD_VALUE;
905 }
906 }
907 return OK;
908}
909
910/**
911 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
912 * RGGB for an unknown enum.
913 */
914static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
915 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
916 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
917 cfaEnum);
918 switch(cfa) {
919 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
920 return OpcodeListBuilder::CFA_RGGB;
921 }
922 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
923 return OpcodeListBuilder::CFA_GRBG;
924 }
925 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
926 return OpcodeListBuilder::CFA_GBRG;
927 }
928 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
929 return OpcodeListBuilder::CFA_BGGR;
930 }
931 default: {
932 return OpcodeListBuilder::CFA_RGGB;
933 }
934 }
935}
936
937/**
938 * For each color plane, find the corresponding noise profile coefficients given in the
939 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
940 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
941 *
942 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
943 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
944 * coefficients.
945 * numChannels - the number of noise profile coefficient pairs and color channels given in
946 * the perChannelNoiseProfile and cfa arguments, respectively.
947 * planeColors - the color planes in the noise profile output.
948 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
949 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
950 *
951 * returns OK, or a negative error code on failure.
952 */
953static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
954 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
955 /*out*/double* noiseProfile) {
956
957 for (size_t p = 0; p < numPlanes; ++p) {
958 size_t S = p * 2;
959 size_t O = p * 2 + 1;
960
961 noiseProfile[S] = 0;
962 noiseProfile[O] = 0;
963 bool uninitialized = true;
964 for (size_t c = 0; c < numChannels; ++c) {
965 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
966 noiseProfile[S] = perChannelNoiseProfile[c * 2];
967 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
968 uninitialized = false;
969 }
970 }
971 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800972 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
973 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700974 return BAD_VALUE;
975 }
976 }
977 return OK;
978}
979
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -0700980static void undistort(/*inout*/double& x, /*inout*/double& y,
981 const std::array<float, 6>& distortion,
982 const float cx, const float cy, const float f) {
983 double xp = (x - cx) / f;
984 double yp = (y - cy) / f;
985
986 double x2 = xp * xp;
987 double y2 = yp * yp;
988 double r2 = x2 + y2;
989 double xy2 = 2.0 * xp * yp;
990
991 const float k0 = distortion[0];
992 const float k1 = distortion[1];
993 const float k2 = distortion[2];
994 const float k3 = distortion[3];
995 const float p1 = distortion[4];
996 const float p2 = distortion[5];
997
998 double kr = k0 + ((k3 * r2 + k2) * r2 + k1) * r2;
999 double xpp = xp * kr + p1 * xy2 + p2 * (r2 + 2.0 * x2);
1000 double ypp = yp * kr + p1 * (r2 + 2.0 * y2) + p2 * xy2;
1001
1002 x = xpp * f + cx;
1003 y = ypp * f + cy;
1004 return;
1005}
1006
1007static inline bool unDistortWithinPreCorrArray(
1008 double x, double y,
1009 const std::array<float, 6>& distortion,
1010 const float cx, const float cy, const float f,
1011 int preCorrW, int preCorrH) {
1012 undistort(x, y, distortion, cx, cy, f);
1013 if (x < 0.0 || y < 0.0 || x > preCorrW - 1 || y > preCorrH - 1) {
1014 return false;
1015 }
1016 return true;
1017}
1018
1019static inline bool boxWithinPrecorrectionArray(
1020 int left, int top, int right, int bottom,
1021 const std::array<float, 6>& distortion,
1022 const float& cx, const float& cy, const float& f,
1023 const int& preCorrW, const int& preCorrH){
1024 // Top row
1025 if (!unDistortWithinPreCorrArray(left, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
1026 return false;
1027 }
1028
1029 if (!unDistortWithinPreCorrArray(cx, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
1030 return false;
1031 }
1032
1033 if (!unDistortWithinPreCorrArray(right, top, distortion, cx, cy, f, preCorrW, preCorrH)) {
1034 return false;
1035 }
1036
1037 // Middle row
1038 if (!unDistortWithinPreCorrArray(left, cy, distortion, cx, cy, f, preCorrW, preCorrH)) {
1039 return false;
1040 }
1041
1042 if (!unDistortWithinPreCorrArray(right, cy, distortion, cx, cy, f, preCorrW, preCorrH)) {
1043 return false;
1044 }
1045
1046 // Bottom row
1047 if (!unDistortWithinPreCorrArray(left, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
1048 return false;
1049 }
1050
1051 if (!unDistortWithinPreCorrArray(cx, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
1052 return false;
1053 }
1054
1055 if (!unDistortWithinPreCorrArray(right, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) {
1056 return false;
1057 }
1058 return true;
1059}
1060
1061static inline bool scaledBoxWithinPrecorrectionArray(
1062 double scale/*must be <= 1.0*/,
1063 const std::array<float, 6>& distortion,
1064 const float cx, const float cy, const float f,
1065 const int preCorrW, const int preCorrH){
1066
1067 double left = cx * (1.0 - scale);
1068 double right = (preCorrW - 1) * scale + cx * (1.0 - scale);
1069 double top = cy * (1.0 - scale);
1070 double bottom = (preCorrH - 1) * scale + cy * (1.0 - scale);
1071
1072 return boxWithinPrecorrectionArray(left, top, right, bottom,
1073 distortion, cx, cy, f, preCorrW, preCorrH);
1074}
1075
1076static status_t findPostCorrectionScale(
1077 double stepSize, double minScale,
1078 const std::array<float, 6>& distortion,
1079 const float cx, const float cy, const float f,
1080 const int preCorrW, const int preCorrH,
1081 /*out*/ double* outScale) {
1082 if (outScale == nullptr) {
1083 ALOGE("%s: outScale must not be null", __FUNCTION__);
1084 return BAD_VALUE;
1085 }
1086
1087 for (double scale = 1.0; scale > minScale; scale -= stepSize) {
1088 if (scaledBoxWithinPrecorrectionArray(
1089 scale, distortion, cx, cy, f, preCorrW, preCorrH)) {
1090 *outScale = scale;
1091 return OK;
1092 }
1093 }
1094 ALOGE("%s: cannot find cropping scale for lens distortion: stepSize %f, minScale %f",
1095 __FUNCTION__, stepSize, minScale);
1096 return BAD_VALUE;
1097}
1098
1099// Apply a scale factor to distortion coefficients so that the image is zoomed out and all pixels
1100// are sampled within the precorrection array
1101static void normalizeLensDistortion(
1102 /*inout*/std::array<float, 6>& distortion,
1103 float cx, float cy, float f, int preCorrW, int preCorrH) {
1104 ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)",
1105 __FUNCTION__, distortion[0], distortion[1], distortion[2],
1106 distortion[3], distortion[4], distortion[5],
1107 cx, cy, f, preCorrW, preCorrH);
1108
1109 // Only update distortion coeffients if we can find a good bounding box
1110 double scale = 1.0;
1111 if (OK == findPostCorrectionScale(0.002, 0.5,
1112 distortion, cx, cy, f, preCorrW, preCorrH,
1113 /*out*/&scale)) {
1114 ALOGV("%s: scaling distortion coefficients by %f", __FUNCTION__, scale);
1115 // The formula:
1116 // xc = xi * (k0 + k1*r^2 + k2*r^4 + k3*r^6) + k4 * (2*xi*yi) + k5 * (r^2 + 2*xi^2)
1117 // To create effective zoom we want to replace xi by xi *m, yi by yi*m and r^2 by r^2*m^2
1118 // Factor the extra m power terms into k0~k6
1119 std::array<float, 6> scalePowers = {1, 3, 5, 7, 2, 2};
1120 for (size_t i = 0; i < 6; i++) {
1121 distortion[i] *= pow(scale, scalePowers[i]);
1122 }
1123 }
1124 return;
1125}
1126
Ruben Brunk47e91f22014-05-28 18:38:42 -07001127// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -07001128extern "C" {
1129
Ruben Brunk47e91f22014-05-28 18:38:42 -07001130static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001131 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001132 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -07001133 gDngCreatorClassInfo.mNativeContext));
1134}
1135
Ruben Brunk47e91f22014-05-28 18:38:42 -07001136static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001137 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001138 NativeContext* current = DngCreator_getNativeContext(env, thiz);
1139
Ruben Brunk20796122015-07-21 17:51:54 -07001140 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001141 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001142 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001143
Ruben Brunkf967a542014-04-28 16:31:11 -07001144 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001145 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001146 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001147
Ruben Brunkf967a542014-04-28 16:31:11 -07001148 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001149 reinterpret_cast<jlong>(context.get()));
1150}
1151
Ruben Brunkf967a542014-04-28 16:31:11 -07001152static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
1153 ALOGV("%s:", __FUNCTION__);
1154
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001155 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1156 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001157
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001158 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1159 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1160 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001161
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001162 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1163 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1164 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001165
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001166 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1167 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1168 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001169}
1170
1171static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001172 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001173 ALOGV("%s:", __FUNCTION__);
1174 CameraMetadata characteristics;
1175 CameraMetadata results;
1176 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1177 jniThrowException(env, "java/lang/AssertionError",
1178 "No native metadata defined for camera characteristics.");
1179 return;
1180 }
1181 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1182 jniThrowException(env, "java/lang/AssertionError",
1183 "No native metadata defined for capture results.");
1184 return;
1185 }
1186
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001187 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001188
1189 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1190
1191 size_t len = strlen(captureTime) + 1;
1192 if (len != NativeContext::DATETIME_COUNT) {
1193 jniThrowException(env, "java/lang/IllegalArgumentException",
1194 "Formatted capture time string length is not required 20 characters");
1195 return;
1196 }
1197
1198 nativeContext->setCaptureTime(String8(captureTime));
1199
1200 DngCreator_setNativeContext(env, thiz, nativeContext);
1201}
1202
1203static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1204 uint32_t imageHeight) {
1205
1206 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1207
1208 if (nativeContext == nullptr) {
1209 jniThrowException(env, "java/lang/AssertionError",
1210 "No native context, must call init before other operations.");
1211 return nullptr;
1212 }
1213
1214 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1215 CameraMetadata results = *(nativeContext->getResult());
1216
1217 sp<TiffWriter> writer = new TiffWriter();
1218
1219 uint32_t preWidth = 0;
1220 uint32_t preHeight = 0;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001221 uint8_t colorFilter = 0;
1222 bool isBayer = true;
Ruben Brunk20796122015-07-21 17:51:54 -07001223 {
1224 // Check dimensions
1225 camera_metadata_entry entry =
1226 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1227 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1228 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1229 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1230
1231 camera_metadata_entry pixelArrayEntry =
1232 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1233 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1234 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1235
1236 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001237 (imageWidth == pixWidth && imageHeight == pixHeight))) {
Ruben Brunk20796122015-07-21 17:51:54 -07001238 jniThrowException(env, "java/lang/AssertionError",
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001239 "Height and width of image buffer did not match height and width of"
Ruben Brunk20796122015-07-21 17:51:54 -07001240 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1241 return nullptr;
1242 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001243
1244 camera_metadata_entry colorFilterEntry =
1245 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1246 colorFilter = colorFilterEntry.data.u8[0];
1247 camera_metadata_entry capabilitiesEntry =
1248 characteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
1249 size_t capsCount = capabilitiesEntry.count;
1250 uint8_t* caps = capabilitiesEntry.data.u8;
1251 if (std::find(caps, caps+capsCount, ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME)
1252 != caps+capsCount) {
1253 isBayer = false;
1254 } else if (colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO ||
1255 colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
1256 jniThrowException(env, "java/lang/AssertionError",
1257 "A camera device with MONO/NIR color filter must have MONOCHROME capability.");
1258 return nullptr;
1259 }
Ruben Brunk20796122015-07-21 17:51:54 -07001260 }
1261
Ruben Brunkf967a542014-04-28 16:31:11 -07001262 writer->addIfd(TIFF_IFD_0);
1263
1264 status_t err = OK;
1265
1266 const uint32_t samplesPerPixel = 1;
1267 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001268
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001269 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_NONE;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001270 uint8_t cfaPlaneColor[3] = {0, 1, 2};
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001271 camera_metadata_entry cfaEntry =
1272 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1273 BAIL_IF_EMPTY_RET_NULL_SP(cfaEntry, env, TAG_CFAPATTERN, writer);
1274 uint8_t cfaEnum = cfaEntry.data.u8[0];
Ruben Brunkf967a542014-04-28 16:31:11 -07001275
1276 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001277 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001278
1279 // Setup main image tags
1280
Ruben Brunkf967a542014-04-28 16:31:11 -07001281 {
1282 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001283 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001284 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1285 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001286 }
1287
1288 {
1289 // Set subfiletype
1290 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001291 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1292 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001293 }
1294
1295 {
1296 // Set bits per sample
1297 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001298 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001299 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001300 }
1301
1302 {
1303 // Set compression
1304 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001305 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1306 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001307 }
1308
1309 {
1310 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001311 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1312 env, TAG_IMAGEWIDTH, writer);
1313 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1314 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001315 }
1316
1317 {
1318 // Set photometric interpretation
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001319 uint16_t interpretation = isBayer ? 32803 /* CFA */ :
1320 34892; /* Linear Raw */;
Ruben Brunk20796122015-07-21 17:51:54 -07001321 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1322 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001323 }
1324
1325 {
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001326 uint16_t repeatDim[2] = {2, 2};
1327 if (!isBayer) {
1328 repeatDim[0] = repeatDim[1] = 1;
1329 }
1330 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1331 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
1332
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001333 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001334 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001335 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1336 uint32_t blackLevelRational[8] = {0};
1337 if (entry.count != 0) {
1338 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1339 for (size_t i = 0; i < entry.count; i++) {
1340 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1341 blackLevelRational[i * 2 + 1] = 100;
1342 }
1343 } else {
1344 // Fall back to static black level which is guaranteed
1345 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1346 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1347 for (size_t i = 0; i < entry.count; i++) {
1348 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1349 blackLevelRational[i * 2 + 1] = 1;
1350 }
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001351 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001352 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, repeatDim[0]*repeatDim[1],
1353 blackLevelRational, TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001354 }
1355
1356 {
1357 // Set samples per pixel
1358 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001359 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001360 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001361 }
1362
1363 {
1364 // Set planar configuration
1365 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001366 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1367 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001368 }
1369
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001370 // All CFA pattern tags are not necessary for monochrome cameras.
1371 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001372 // Set CFA pattern dimensions
1373 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001374 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1375 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001376
Ruben Brunkf967a542014-04-28 16:31:11 -07001377 // Set CFA pattern
Ruben Brunkd70132c2014-08-22 16:24:49 -07001378 const int cfaLength = 4;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001379 uint8_t cfa[cfaLength];
1380 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1381 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1382 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001383 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001384
Ruben Brunk20796122015-07-21 17:51:54 -07001385 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1386 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001387
1388 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001389
Ruben Brunkf967a542014-04-28 16:31:11 -07001390 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001391 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1392 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001393
Ruben Brunkf967a542014-04-28 16:31:11 -07001394 // Set CFA layout
1395 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001396 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001397 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001398 }
1399
1400 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001401 // image description
1402 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001403 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1404 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001405 }
1406
1407 {
1408 // make
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001409 // Use "" to represent unknown make as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001410 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1411 uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001412
Ruben Brunk20796122015-07-21 17:51:54 -07001413 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001414 reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE,
1415 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001416 }
1417
1418 {
1419 // model
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001420 // Use "" to represent unknown model as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001421 std::string model = GetProperty("ro.product.model", "");
1422 uint32_t count = static_cast<uint32_t>(model.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001423
Ruben Brunk20796122015-07-21 17:51:54 -07001424 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001425 reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL,
1426 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001427 }
1428
1429 {
1430 // x resolution
1431 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001432 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001433 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001434
1435 // y resolution
1436 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001437 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001438 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001439
1440 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001441 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001442 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001443 }
1444
1445 {
1446 // software
Tom Cherry93099e42017-10-11 13:44:21 -07001447 std::string software = GetProperty("ro.build.fingerprint", "");
1448 uint32_t count = static_cast<uint32_t>(software.size()) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001449 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001450 reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE,
1451 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001452 }
1453
Ruben Brunk20796122015-07-21 17:51:54 -07001454 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001455 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001456 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001457
Ruben Brunk20796122015-07-21 17:51:54 -07001458 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1459 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001460 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1461 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001462 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001463 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001464
1465 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001466 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1467 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001468 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1469 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001470 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001471 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001472 }
1473
1474 {
1475 // TIFF/EP standard id
1476 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001477 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001478 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001479 }
1480
1481 {
1482 // copyright
1483 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001484 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001485 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001486 }
1487
1488 {
1489 // exposure time
1490 camera_metadata_entry entry =
1491 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001492 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001493
1494 int64_t exposureTime = *(entry.data.i64);
1495
1496 if (exposureTime < 0) {
1497 // Should be unreachable
1498 jniThrowException(env, "java/lang/IllegalArgumentException",
1499 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001500 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001501 }
1502
1503 // Ensure exposure time doesn't overflow (for exposures > 4s)
1504 uint32_t denominator = 1000000000;
1505 while (exposureTime > UINT32_MAX) {
1506 exposureTime >>= 1;
1507 denominator >>= 1;
1508 if (denominator == 0) {
1509 // Should be unreachable
1510 jniThrowException(env, "java/lang/IllegalArgumentException",
1511 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001512 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001513 }
1514 }
1515
1516 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001517 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001518 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001519
1520 }
1521
1522 {
1523 // ISO speed ratings
1524 camera_metadata_entry entry =
1525 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001526 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001527
1528 int32_t tempIso = *(entry.data.i32);
1529 if (tempIso < 0) {
1530 jniThrowException(env, "java/lang/IllegalArgumentException",
1531 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001532 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001533 }
1534
1535 if (tempIso > UINT16_MAX) {
1536 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1537 tempIso = UINT16_MAX;
1538 }
1539
1540 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001541 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001542 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001543 }
1544
1545 {
Emilian Peev7ca13712017-04-05 15:42:22 +01001546 // Baseline exposure
1547 camera_metadata_entry entry =
1548 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
1549 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
1550
1551 // post RAW gain should be boostValue / 100
1552 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
1553 // Baseline exposure should be in EV units so log2(gain) =
1554 // log10(gain)/log10(2)
1555 double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
1556 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
1557 100 };
1558 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
1559 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
1560 }
1561
1562 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001563 // focal length
1564 camera_metadata_entry entry =
1565 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001566 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001567
1568 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001569 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001570 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001571 }
1572
1573 {
1574 // f number
1575 camera_metadata_entry entry =
1576 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001577 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001578
1579 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001580 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001581 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001582 }
1583
1584 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001585 // Set DNG version information
1586 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001587 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001588 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001589
1590 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001591 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1592 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001593 }
1594
1595 {
1596 // Set whitelevel
1597 camera_metadata_entry entry =
1598 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001599 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001600 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001601 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1602 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001603 }
1604
1605 {
1606 // Set default scale
1607 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001608 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1609 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001610 }
1611
1612 bool singleIlluminant = false;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001613 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001614 // Set calibration illuminants
1615 camera_metadata_entry entry1 =
1616 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001617 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001618 camera_metadata_entry entry2 =
1619 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1620 if (entry2.count == 0) {
1621 singleIlluminant = true;
1622 }
1623 uint16_t ref1 = entry1.data.u8[0];
1624
Ruben Brunk20796122015-07-21 17:51:54 -07001625 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001626 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001627
1628 if (!singleIlluminant) {
1629 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001630 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001631 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001632 }
1633 }
1634
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001635 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001636 // Set color transforms
1637 camera_metadata_entry entry1 =
1638 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001639 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001640
1641 int32_t colorTransform1[entry1.count * 2];
1642
1643 size_t ctr = 0;
1644 for(size_t i = 0; i < entry1.count; ++i) {
1645 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1646 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1647 }
1648
Ruben Brunk20796122015-07-21 17:51:54 -07001649 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1650 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001651
1652 if (!singleIlluminant) {
1653 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001654 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001655 int32_t colorTransform2[entry2.count * 2];
1656
1657 ctr = 0;
1658 for(size_t i = 0; i < entry2.count; ++i) {
1659 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1660 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1661 }
1662
Ruben Brunk20796122015-07-21 17:51:54 -07001663 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1664 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001665 }
1666 }
1667
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001668 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001669 // Set calibration transforms
1670 camera_metadata_entry entry1 =
1671 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001672 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001673
1674 int32_t calibrationTransform1[entry1.count * 2];
1675
1676 size_t ctr = 0;
1677 for(size_t i = 0; i < entry1.count; ++i) {
1678 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1679 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1680 }
1681
Ruben Brunk20796122015-07-21 17:51:54 -07001682 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001683 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001684
1685 if (!singleIlluminant) {
1686 camera_metadata_entry entry2 =
1687 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001688 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001689 int32_t calibrationTransform2[entry2.count * 2];
1690
1691 ctr = 0;
1692 for(size_t i = 0; i < entry2.count; ++i) {
1693 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1694 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1695 }
1696
Ruben Brunk20796122015-07-21 17:51:54 -07001697 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001698 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001699 }
1700 }
1701
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001702 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001703 // Set forward transforms
1704 camera_metadata_entry entry1 =
1705 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001706 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001707
1708 int32_t forwardTransform1[entry1.count * 2];
1709
1710 size_t ctr = 0;
1711 for(size_t i = 0; i < entry1.count; ++i) {
1712 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1713 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1714 }
1715
Ruben Brunk20796122015-07-21 17:51:54 -07001716 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1717 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001718
1719 if (!singleIlluminant) {
1720 camera_metadata_entry entry2 =
1721 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001722 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001723 int32_t forwardTransform2[entry2.count * 2];
1724
1725 ctr = 0;
1726 for(size_t i = 0; i < entry2.count; ++i) {
1727 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1728 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1729 }
1730
Ruben Brunk20796122015-07-21 17:51:54 -07001731 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1732 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001733 }
1734 }
1735
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001736 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001737 // Set camera neutral
1738 camera_metadata_entry entry =
1739 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001740 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001741 uint32_t cameraNeutral[entry.count * 2];
1742
1743 size_t ctr = 0;
1744 for(size_t i = 0; i < entry.count; ++i) {
1745 cameraNeutral[ctr++] =
1746 static_cast<uint32_t>(entry.data.r[i].numerator);
1747 cameraNeutral[ctr++] =
1748 static_cast<uint32_t>(entry.data.r[i].denominator);
1749 }
1750
Ruben Brunk20796122015-07-21 17:51:54 -07001751 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001752 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001753 }
1754
Ruben Brunkf967a542014-04-28 16:31:11 -07001755
1756 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001757 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001758 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001759 return nullptr;
1760 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001761 camera_metadata_entry entry =
1762 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001763 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001764 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1765 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1766 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1767 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001768
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001769 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1770 // relative to the pixel array.
1771 if (imageWidth == width && imageHeight == height) {
1772 xmin = 0;
1773 ymin = 0;
1774 }
1775
Ruben Brunk20796122015-07-21 17:51:54 -07001776 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1777 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1778 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001779 }
1780
1781 {
1782 // Setup unique camera model tag
Tom Cherry93099e42017-10-11 13:44:21 -07001783 std::string model = GetProperty("ro.product.model", "");
1784 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1785 std::string brand = GetProperty("ro.product.brand", "");
Ruben Brunkf967a542014-04-28 16:31:11 -07001786
Tom Cherry93099e42017-10-11 13:44:21 -07001787 String8 cameraModel(model.c_str());
Ruben Brunkf967a542014-04-28 16:31:11 -07001788 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001789 cameraModel += manufacturer.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001790 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001791 cameraModel += brand.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001792
Ruben Brunk20796122015-07-21 17:51:54 -07001793 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001794 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001795 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001796 }
1797
1798 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001799 // Setup sensor noise model
1800 camera_metadata_entry entry =
1801 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1802
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001803 const status_t numPlaneColors = isBayer ? 3 : 1;
1804 const status_t numCfaChannels = isBayer ? 4 : 1;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001805
1806 uint8_t cfaOut[numCfaChannels];
1807 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1808 jniThrowException(env, "java/lang/IllegalArgumentException",
1809 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001810 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001811 }
1812
1813 double noiseProfile[numPlaneColors * 2];
1814
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001815 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001816 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001817 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1818 "in characteristics, no noise profile tag written...",
1819 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001820 } else {
1821 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1822 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1823
Ruben Brunk20796122015-07-21 17:51:54 -07001824 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1825 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1826 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001827 } else {
1828 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1829 " tag written...", __FUNCTION__);
1830 }
1831 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001832 } else {
1833 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1834 __FUNCTION__);
1835 }
1836 }
1837
1838 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001839 // Set up opcode List 2
1840 OpcodeListBuilder builder;
1841 status_t err = OK;
1842
1843 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001844 camera_metadata_entry entry1 =
1845 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001846
1847 uint32_t lsmWidth = 0;
1848 uint32_t lsmHeight = 0;
1849
1850 if (entry1.count != 0) {
1851 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1852 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1853 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001854
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001855 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001856
Ruben Brunk20796122015-07-21 17:51:54 -07001857 camera_metadata_entry entry =
1858 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1859 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1860 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1861 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1862 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1863 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001864 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001865 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001866 err = builder.addGainMapsForMetadata(lsmWidth,
1867 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001868 0,
1869 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001870 height,
1871 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001872 opcodeCfaLayout,
1873 entry2.data.f);
1874 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001875 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1876 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001877 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001878 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001879 }
1880
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001881 // Hot pixel map is specific to bayer camera per DNG spec.
1882 if (isBayer) {
1883 // Set up bad pixel correction list
1884 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001885
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001886 if ((entry3.count % 2) != 0) {
1887 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1888 __FUNCTION__);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001889 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1890 return nullptr;
1891 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001892
1893 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1894 std::vector<uint32_t> v;
1895 for (size_t i = 0; i < entry3.count; i += 2) {
1896 int32_t x = entry3.data.i32[i];
1897 int32_t y = entry3.data.i32[i + 1];
1898 x -= static_cast<int32_t>(xmin);
1899 y -= static_cast<int32_t>(ymin);
1900 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1901 static_cast<uint32_t>(y) >= height) {
1902 continue;
1903 }
1904 v.push_back(x);
1905 v.push_back(y);
1906 }
1907 const uint32_t* badPixels = &v[0];
1908 uint32_t badPixelCount = v.size();
1909
1910 if (badPixelCount > 0) {
1911 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1912
1913 if (err != OK) {
1914 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1915 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1916 return nullptr;
1917 }
1918 }
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001919 }
1920
Sam Hasinoff58398342018-08-01 18:24:59 -07001921 if (builder.getCount() > 0) {
1922 size_t listSize = builder.getSize();
1923 uint8_t opcodeListBuf[listSize];
1924 err = builder.buildOpList(opcodeListBuf);
1925 if (err == OK) {
1926 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize,
1927 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1928 } else {
1929 ALOGE("%s: Could not build list of opcodes for lens shading map and bad pixel "
1930 "correction.", __FUNCTION__);
1931 jniThrowRuntimeException(env, "failed to construct opcode list for lens shading "
1932 "map and bad pixel correction");
1933 return nullptr;
1934 }
Ruben Brunkfe816622015-06-16 23:03:09 -07001935 }
1936 }
1937
1938 {
1939 // Set up opcode List 3
1940 OpcodeListBuilder builder;
1941 status_t err = OK;
1942
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001943 // Set up rectilinear distortion correction
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001944 std::array<float, 6> distortion = {1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001945 bool gotDistortion = false;
1946
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001947 camera_metadata_entry entry4 =
1948 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1949
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001950 if (entry4.count == 5) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001951 float cx = entry4.data.f[/*c_x*/2];
1952 float cy = entry4.data.f[/*c_y*/3];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001953 // Assuming f_x = f_y, or at least close enough.
1954 // Also assuming s = 0, or at least close enough.
1955 float f = entry4.data.f[/*f_x*/0];
1956
1957 camera_metadata_entry entry3 =
1958 results.find(ANDROID_LENS_DISTORTION);
1959 if (entry3.count == 5) {
1960 gotDistortion = true;
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001961
1962
1963 // Scale the distortion coefficients to create a zoom in warpped image so that all
1964 // pixels are drawn within input image.
1965 for (size_t i = 0; i < entry3.count; i++) {
1966 distortion[i+1] = entry3.data.f[i];
1967 }
1968
1969 // TODO b/118690688: deal with the case where RAW size != preCorrSize
1970 if (preWidth == imageWidth && preHeight == imageHeight) {
1971 normalizeLensDistortion(distortion, cx, cy, f, preWidth, preHeight);
1972 }
1973
Yin-Chia Yeh47849ab2018-06-04 09:23:14 -07001974 float m_x = std::fmaxf(preWidth-1 - cx, cx);
1975 float m_y = std::fmaxf(preHeight-1 - cy, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001976 float m_sq = m_x*m_x + m_y*m_y;
1977 float m = sqrtf(m_sq); // distance to farthest corner from optical center
1978 float f_sq = f * f;
1979 // Conversion factors from Camera2 K factors for new LENS_DISTORTION field
1980 // to DNG spec.
1981 //
1982 // Camera2 / OpenCV assume distortion is applied in a space where focal length
1983 // is factored out, while DNG assumes a normalized space where the distance
1984 // from optical center to the farthest corner is 1.
1985 // Scale from camera2 to DNG spec accordingly.
1986 // distortion[0] is always 1 with the new LENS_DISTORTION field.
1987 const double convCoeff[5] = {
1988 m_sq / f_sq,
1989 pow(m_sq, 2) / pow(f_sq, 2),
1990 pow(m_sq, 3) / pow(f_sq, 3),
1991 m / f,
1992 m / f
1993 };
1994 for (size_t i = 0; i < entry3.count; i++) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001995 distortion[i+1] *= convCoeff[i];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001996 }
1997 } else {
1998 entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION);
1999 if (entry3.count == 6) {
2000 gotDistortion = true;
2001 // Conversion factors from Camera2 K factors to DNG spec. K factors:
2002 //
2003 // Note: these are necessary because our unit system assumes a
2004 // normalized max radius of sqrt(2), whereas the DNG spec's
2005 // WarpRectilinear opcode assumes a normalized max radius of 1.
2006 // Thus, each K coefficient must include the domain scaling
2007 // factor (the DNG domain is scaled by sqrt(2) to emulate the
2008 // domain used by the Camera2 specification).
2009 const double convCoeff[6] = {
2010 sqrt(2),
2011 2 * sqrt(2),
2012 4 * sqrt(2),
2013 8 * sqrt(2),
2014 2,
2015 2
2016 };
2017 for (size_t i = 0; i < entry3.count; i++) {
2018 distortion[i] = entry3.data.f[i] * convCoeff[i];
2019 }
2020 }
2021 }
2022 if (gotDistortion) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07002023 err = builder.addWarpRectilinearForMetadata(
2024 distortion.data(), preWidth, preHeight, cx, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07002025 if (err != OK) {
2026 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
2027 jniThrowRuntimeException(env, "failed to add distortion correction.");
2028 return nullptr;
2029 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07002030 }
2031 }
2032
Sam Hasinoff58398342018-08-01 18:24:59 -07002033 if (builder.getCount() > 0) {
2034 size_t listSize = builder.getSize();
2035 uint8_t opcodeListBuf[listSize];
2036 err = builder.buildOpList(opcodeListBuf);
2037 if (err == OK) {
2038 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize,
2039 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
2040 } else {
2041 ALOGE("%s: Could not build list of opcodes for distortion correction.",
2042 __FUNCTION__);
2043 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
2044 " correction");
2045 return nullptr;
2046 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002047 }
2048 }
2049
Ruben Brunk20796122015-07-21 17:51:54 -07002050 {
2051 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08002052 // Note: There's only one orientation field for the whole file, in IFD0
2053 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07002054 uint16_t orientation = nativeContext->getOrientation();
2055 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
2056 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07002057
Ruben Brunk47e91f22014-05-28 18:38:42 -07002058 }
2059
Ruben Brunk20796122015-07-21 17:51:54 -07002060 if (nativeContext->hasDescription()){
2061 // Set Description
2062 String8 description = nativeContext->getDescription();
2063 size_t len = description.bytes() + 1;
2064 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
2065 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
2066 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
2067 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002068 }
2069 }
2070
Ruben Brunk20796122015-07-21 17:51:54 -07002071 if (nativeContext->hasGpsData()) {
2072 // Set GPS tags
2073 GpsData gpsData = nativeContext->getGpsData();
2074 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
2075 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
2076 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
2077 TIFF_IFD_0);
2078 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
2079 return nullptr;
2080 }
2081 }
2082
2083 {
2084 uint8_t version[] = {2, 3, 0, 0};
2085 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
2086 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
2087 }
2088
2089 {
2090 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
2091 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
2092 TAG_GPSLATITUDEREF, writer);
2093 }
2094
2095 {
2096 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
2097 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
2098 TAG_GPSLONGITUDEREF, writer);
2099 }
2100
2101 {
2102 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
2103 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
2104 }
2105
2106 {
2107 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
2108 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
2109 }
2110
2111 {
2112 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
2113 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
2114 }
2115
2116 {
2117 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
2118 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
2119 TAG_GPSDATESTAMP, writer);
2120 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002121 }
2122
Ruben Brunk47e91f22014-05-28 18:38:42 -07002123
Ruben Brunk20796122015-07-21 17:51:54 -07002124 if (nativeContext->hasThumbnail()) {
2125 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
2126 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
2127 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
2128 TIFF_IFD_0);
2129 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
2130 return nullptr;
2131 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002132 }
2133
2134 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002135 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07002136 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002137 tagsToMove.add(TAG_BITSPERSAMPLE);
2138 tagsToMove.add(TAG_COMPRESSION);
2139 tagsToMove.add(TAG_IMAGEWIDTH);
2140 tagsToMove.add(TAG_IMAGELENGTH);
2141 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
2142 tagsToMove.add(TAG_BLACKLEVEL);
2143 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
2144 tagsToMove.add(TAG_SAMPLESPERPIXEL);
2145 tagsToMove.add(TAG_PLANARCONFIGURATION);
Shuzhen Wanga8d36032018-10-15 12:01:53 -07002146 if (isBayer) {
2147 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
2148 tagsToMove.add(TAG_CFAPATTERN);
2149 tagsToMove.add(TAG_CFAPLANECOLOR);
2150 tagsToMove.add(TAG_CFALAYOUT);
2151 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002152 tagsToMove.add(TAG_XRESOLUTION);
2153 tagsToMove.add(TAG_YRESOLUTION);
2154 tagsToMove.add(TAG_RESOLUTIONUNIT);
2155 tagsToMove.add(TAG_WHITELEVEL);
2156 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002157 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
2158 tagsToMove.add(TAG_DEFAULTCROPSIZE);
Emilian Peeva7503b92018-08-13 12:46:05 +01002159
2160 if (nullptr != writer->getEntry(TAG_OPCODELIST2, TIFF_IFD_0).get()) {
2161 tagsToMove.add(TAG_OPCODELIST2);
2162 }
2163
2164 if (nullptr != writer->getEntry(TAG_OPCODELIST3, TIFF_IFD_0).get()) {
2165 tagsToMove.add(TAG_OPCODELIST3);
2166 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002167
2168 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
2169 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07002170 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002171 }
2172
Ruben Brunk20796122015-07-21 17:51:54 -07002173 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07002174
Ruben Brunk20796122015-07-21 17:51:54 -07002175 {
2176 // Set photometric interpretation
2177 uint16_t interpretation = 2; // RGB
2178 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
2179 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002180 }
Ruben Brunk20796122015-07-21 17:51:54 -07002181
2182 {
2183 // Set planar configuration
2184 uint16_t config = 1; // Chunky
2185 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
2186 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
2187 }
2188
2189 {
2190 // Set samples per pixel
2191 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
2192 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
2193 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
2194 }
2195
2196 {
2197 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08002198 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
2199 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
2200 BAIL_IF_INVALID_RET_NULL_SP(
2201 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07002202 env, TAG_BITSPERSAMPLE, writer);
2203 }
2204
2205 {
2206 // Set subfiletype
2207 uint32_t subfileType = 1; // Thumbnail image
2208 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
2209 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
2210 }
2211
2212 {
2213 // Set compression
2214 uint16_t compression = 1; // None
2215 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
2216 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
2217 }
2218
2219 {
2220 // Set dimensions
2221 uint32_t uWidth = nativeContext->getThumbnailWidth();
2222 uint32_t uHeight = nativeContext->getThumbnailHeight();
2223 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
2224 env, TAG_IMAGEWIDTH, writer);
2225 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
2226 env, TAG_IMAGELENGTH, writer);
2227 }
2228
2229 {
2230 // x resolution
2231 uint32_t xres[] = { 72, 1 }; // default 72 ppi
2232 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
2233 env, TAG_XRESOLUTION, writer);
2234
2235 // y resolution
2236 uint32_t yres[] = { 72, 1 }; // default 72 ppi
2237 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
2238 env, TAG_YRESOLUTION, writer);
2239
2240 uint16_t unit = 2; // inches
2241 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
2242 env, TAG_RESOLUTIONUNIT, writer);
2243 }
2244 }
2245
2246 if (writer->addStrip(TIFF_IFD_0) != OK) {
2247 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
2248 jniThrowException(env, "java/lang/IllegalStateException",
2249 "Failed to setup thumbnail strip tags.");
2250 return nullptr;
2251 }
2252
2253 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002254 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
2255 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
2256 jniThrowException(env, "java/lang/IllegalStateException",
2257 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07002258 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002259 }
2260 }
Ruben Brunk20796122015-07-21 17:51:54 -07002261 return writer;
2262}
2263
2264static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2265 ALOGV("%s:", __FUNCTION__);
2266 DngCreator_setNativeContext(env, thiz, nullptr);
2267}
2268
2269static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2270 ALOGV("%s:", __FUNCTION__);
2271
2272 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2273 if (context == nullptr) {
2274 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2275 jniThrowException(env, "java/lang/AssertionError",
2276 "setOrientation called with uninitialized DngCreator");
2277 return;
2278 }
2279
2280 uint16_t orientation = static_cast<uint16_t>(orient);
2281 context->setOrientation(orientation);
2282}
2283
2284static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2285 ALOGV("%s:", __FUNCTION__);
2286
2287 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2288 if (context == nullptr) {
2289 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2290 jniThrowException(env, "java/lang/AssertionError",
2291 "setDescription called with uninitialized DngCreator");
2292 return;
2293 }
2294
2295 const char* desc = env->GetStringUTFChars(description, nullptr);
2296 context->setDescription(String8(desc));
2297 env->ReleaseStringUTFChars(description, desc);
2298}
2299
2300static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2301 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2302 ALOGV("%s:", __FUNCTION__);
2303
2304 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2305 if (context == nullptr) {
2306 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2307 jniThrowException(env, "java/lang/AssertionError",
2308 "setGpsTags called with uninitialized DngCreator");
2309 return;
2310 }
2311
2312 GpsData data;
2313
2314 jsize latLen = env->GetArrayLength(latTag);
2315 jsize longLen = env->GetArrayLength(longTag);
2316 jsize timeLen = env->GetArrayLength(timeTag);
2317 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2318 jniThrowException(env, "java/lang/IllegalArgumentException",
2319 "invalid latitude tag length");
2320 return;
2321 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2322 jniThrowException(env, "java/lang/IllegalArgumentException",
2323 "invalid longitude tag length");
2324 return;
2325 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2326 jniThrowException(env, "java/lang/IllegalArgumentException",
2327 "invalid time tag length");
2328 return;
2329 }
2330
2331 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2332 reinterpret_cast<jint*>(&data.mLatitude));
2333 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2334 reinterpret_cast<jint*>(&data.mLongitude));
2335 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2336 reinterpret_cast<jint*>(&data.mTimestamp));
2337
2338
2339 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2340 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2341 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2342 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2343 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2344 reinterpret_cast<char*>(&data.mDate));
2345 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2346
2347 context->setGpsData(data);
2348}
2349
2350static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2351 jint height) {
2352 ALOGV("%s:", __FUNCTION__);
2353
2354 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2355 if (context == nullptr) {
2356 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2357 jniThrowException(env, "java/lang/AssertionError",
2358 "setThumbnail called with uninitialized DngCreator");
2359 return;
2360 }
2361
2362 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2363 jlong capacity = env->GetDirectBufferCapacity(buffer);
2364 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2365 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2366 "Invalid size %d for thumbnail, expected size was %d",
2367 capacity, fullSize);
2368 return;
2369 }
2370
2371 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2372 if (pixelBytes == nullptr) {
2373 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2374 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2375 return;
2376 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002377
2378 if (!context->setThumbnail(pixelBytes, width, height)) {
2379 jniThrowException(env, "java/lang/IllegalStateException",
2380 "Failed to set thumbnail.");
2381 return;
2382 }
2383}
2384
2385// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002386static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002387 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2388 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002389 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002390 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2391 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2392 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002393 uint32_t rStride = static_cast<uint32_t>(rowStride);
2394 uint32_t pStride = static_cast<uint32_t>(pixStride);
2395 uint32_t uWidth = static_cast<uint32_t>(width);
2396 uint32_t uHeight = static_cast<uint32_t>(height);
2397 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002398
2399 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2400 if(env->ExceptionCheck()) {
2401 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2402 return;
2403 }
2404
Ruben Brunk47e91f22014-05-28 18:38:42 -07002405 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002406 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002407 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2408 jniThrowException(env, "java/lang/AssertionError",
2409 "Write called with uninitialized DngCreator");
2410 return;
2411 }
Ruben Brunk20796122015-07-21 17:51:54 -07002412 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002413
Ruben Brunk20796122015-07-21 17:51:54 -07002414 if (writer.get() == nullptr) {
2415 return;
2416 }
2417
2418 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002419 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002420 return;
2421 }
2422
Ruben Brunk47e91f22014-05-28 18:38:42 -07002423 sp<JniInputByteBuffer> inBuf;
2424 Vector<StripSource*> sources;
2425 sp<DirectStripSource> thumbnailSource;
2426 uint32_t targetIfd = TIFF_IFD_0;
2427
2428 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2429
2430 if (hasThumbnail) {
2431 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2432 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2433 uint32_t thumbWidth = context->getThumbnailWidth();
2434 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2435 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2436 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2437 SAMPLES_PER_RGB_PIXEL);
2438 sources.add(thumbnailSource.get());
2439 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002440 }
2441
Ruben Brunk47e91f22014-05-28 18:38:42 -07002442 if (isDirect) {
2443 size_t fullSize = rStride * uHeight;
2444 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2445 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2446 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2447 "Invalid size %d for Image, size given in metadata is %d at current stride",
2448 capacity, fullSize);
2449 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002450 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002451
Ruben Brunk47e91f22014-05-28 18:38:42 -07002452 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002453 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002454 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2455 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2456 return;
2457 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002458
Ruben Brunk47e91f22014-05-28 18:38:42 -07002459 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2460 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2461 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2462 sources.add(&stripSource);
2463
2464 status_t ret = OK;
2465 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2466 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002467 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002468 jniThrowExceptionFmt(env, "java/io/IOException",
2469 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002470 }
2471 return;
2472 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002473 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002474 inBuf = new JniInputByteBuffer(env, inBuffer);
2475
2476 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2477 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2478 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2479 sources.add(&stripSource);
2480
2481 status_t ret = OK;
2482 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2483 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2484 if (!env->ExceptionCheck()) {
2485 jniThrowExceptionFmt(env, "java/io/IOException",
2486 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002487 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002488 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002489 }
2490 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002491}
2492
2493static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002494 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002495 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002496
2497 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2498 uint32_t pixStride = BYTES_PER_SAMPLE;
2499 uint32_t uWidth = static_cast<uint32_t>(width);
2500 uint32_t uHeight = static_cast<uint32_t>(height);
2501 uint64_t uOffset = static_cast<uint32_t>(offset);
2502
Dan Albert46d84442014-11-18 16:07:51 -08002503 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2504 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2505 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002506
2507 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002508 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002509 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2510 return;
2511 }
2512
Ruben Brunk47e91f22014-05-28 18:38:42 -07002513 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002514 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002515 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2516 jniThrowException(env, "java/lang/AssertionError",
2517 "Write called with uninitialized DngCreator");
2518 return;
2519 }
Ruben Brunk20796122015-07-21 17:51:54 -07002520 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002521
Ruben Brunk20796122015-07-21 17:51:54 -07002522 if (writer.get() == nullptr) {
2523 return;
2524 }
2525
2526 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002527 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002528 return;
2529 }
2530
2531 sp<DirectStripSource> thumbnailSource;
2532 uint32_t targetIfd = TIFF_IFD_0;
2533 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2534 Vector<StripSource*> sources;
2535
2536 if (hasThumbnail) {
2537 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2538 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2539 uint32_t width = context->getThumbnailWidth();
2540 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2541 width, context->getThumbnailHeight(), bytesPerPixel,
2542 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2543 SAMPLES_PER_RGB_PIXEL);
2544 sources.add(thumbnailSource.get());
2545 targetIfd = TIFF_IFD_SUB1;
2546 }
2547
2548 sp<JniInputStream> in = new JniInputStream(env, inStream);
2549
2550 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2551 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2552 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2553 sources.add(&stripSource);
2554
2555 status_t ret = OK;
2556 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2557 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2558 if (!env->ExceptionCheck()) {
2559 jniThrowExceptionFmt(env, "java/io/IOException",
2560 "Encountered error %d while writing file.", ret);
2561 }
2562 return;
2563 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002564}
2565
2566} /*extern "C" */
2567
Daniel Micay76f6a862015-09-19 17:31:01 -04002568static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002569 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2570 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002571 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2572 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002573 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2574 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002575 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2576 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2577 (void*) DngCreator_nativeSetGpsTags},
2578 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2579 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002580 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002581 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002582 (void*) DngCreator_nativeWriteInputStream},
2583};
2584
Ruben Brunkb6079002014-05-22 12:33:54 -07002585int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002586 return RegisterMethodsOrDie(env,
2587 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002588}