blob: 4fad239b5743800539635ad9066fec8676c434fb [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,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001011 const int preCorrW, const int preCorrH, const int xMin, const int yMin) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001012 undistort(x, y, distortion, cx, cy, f);
Shuzhen Wang461f3cf2019-04-19 12:19:01 -07001013 // xMin and yMin are inclusive, and xMax and yMax are exclusive.
1014 int xMax = xMin + preCorrW;
1015 int yMax = yMin + preCorrH;
1016 if (x < xMin || y < yMin || x >= xMax || y >= yMax) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001017 return false;
1018 }
1019 return true;
1020}
1021
1022static inline bool boxWithinPrecorrectionArray(
1023 int left, int top, int right, int bottom,
1024 const std::array<float, 6>& distortion,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001025 const float cx, const float cy, const float f,
1026 const int preCorrW, const int preCorrH, const int xMin, const int yMin){
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001027 // Top row
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001028 if (!unDistortWithinPreCorrArray(left, top,
1029 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001030 return false;
1031 }
1032
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001033 if (!unDistortWithinPreCorrArray(cx, top,
1034 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001035 return false;
1036 }
1037
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001038 if (!unDistortWithinPreCorrArray(right, top,
1039 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001040 return false;
1041 }
1042
1043 // Middle row
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001044 if (!unDistortWithinPreCorrArray(left, cy,
1045 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001046 return false;
1047 }
1048
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001049 if (!unDistortWithinPreCorrArray(right, cy,
1050 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001051 return false;
1052 }
1053
1054 // Bottom row
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001055 if (!unDistortWithinPreCorrArray(left, bottom,
1056 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001057 return false;
1058 }
1059
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001060 if (!unDistortWithinPreCorrArray(cx, bottom,
1061 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001062 return false;
1063 }
1064
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001065 if (!unDistortWithinPreCorrArray(right, bottom,
1066 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001067 return false;
1068 }
1069 return true;
1070}
1071
1072static inline bool scaledBoxWithinPrecorrectionArray(
1073 double scale/*must be <= 1.0*/,
1074 const std::array<float, 6>& distortion,
1075 const float cx, const float cy, const float f,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001076 const int preCorrW, const int preCorrH,
1077 const int xMin, const int yMin){
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001078
1079 double left = cx * (1.0 - scale);
1080 double right = (preCorrW - 1) * scale + cx * (1.0 - scale);
1081 double top = cy * (1.0 - scale);
1082 double bottom = (preCorrH - 1) * scale + cy * (1.0 - scale);
1083
1084 return boxWithinPrecorrectionArray(left, top, right, bottom,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001085 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin);
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001086}
1087
1088static status_t findPostCorrectionScale(
1089 double stepSize, double minScale,
1090 const std::array<float, 6>& distortion,
1091 const float cx, const float cy, const float f,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001092 const int preCorrW, const int preCorrH, const int xMin, const int yMin,
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001093 /*out*/ double* outScale) {
1094 if (outScale == nullptr) {
1095 ALOGE("%s: outScale must not be null", __FUNCTION__);
1096 return BAD_VALUE;
1097 }
1098
1099 for (double scale = 1.0; scale > minScale; scale -= stepSize) {
1100 if (scaledBoxWithinPrecorrectionArray(
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001101 scale, distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin)) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001102 *outScale = scale;
1103 return OK;
1104 }
1105 }
1106 ALOGE("%s: cannot find cropping scale for lens distortion: stepSize %f, minScale %f",
1107 __FUNCTION__, stepSize, minScale);
1108 return BAD_VALUE;
1109}
1110
1111// Apply a scale factor to distortion coefficients so that the image is zoomed out and all pixels
1112// are sampled within the precorrection array
1113static void normalizeLensDistortion(
1114 /*inout*/std::array<float, 6>& distortion,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001115 float cx, float cy, float f, int preCorrW, int preCorrH, int xMin = 0, int yMin = 0) {
1116 ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)"
1117 ", (xmin, ymin, xmax, ymax) (%d, %d, %d, %d)",
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001118 __FUNCTION__, distortion[0], distortion[1], distortion[2],
1119 distortion[3], distortion[4], distortion[5],
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001120 cx, cy, f, preCorrW, preCorrH,
1121 xMin, yMin, xMin + preCorrW - 1, yMin + preCorrH - 1);
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001122
1123 // Only update distortion coeffients if we can find a good bounding box
1124 double scale = 1.0;
1125 if (OK == findPostCorrectionScale(0.002, 0.5,
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001126 distortion, cx, cy, f, preCorrW, preCorrH, xMin, yMin,
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001127 /*out*/&scale)) {
1128 ALOGV("%s: scaling distortion coefficients by %f", __FUNCTION__, scale);
1129 // The formula:
1130 // xc = xi * (k0 + k1*r^2 + k2*r^4 + k3*r^6) + k4 * (2*xi*yi) + k5 * (r^2 + 2*xi^2)
1131 // To create effective zoom we want to replace xi by xi *m, yi by yi*m and r^2 by r^2*m^2
1132 // Factor the extra m power terms into k0~k6
1133 std::array<float, 6> scalePowers = {1, 3, 5, 7, 2, 2};
1134 for (size_t i = 0; i < 6; i++) {
1135 distortion[i] *= pow(scale, scalePowers[i]);
1136 }
1137 }
1138 return;
1139}
1140
Ruben Brunk47e91f22014-05-28 18:38:42 -07001141// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -07001142extern "C" {
1143
Ruben Brunk47e91f22014-05-28 18:38:42 -07001144static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001145 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001146 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -07001147 gDngCreatorClassInfo.mNativeContext));
1148}
1149
Ruben Brunk47e91f22014-05-28 18:38:42 -07001150static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001151 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001152 NativeContext* current = DngCreator_getNativeContext(env, thiz);
1153
Ruben Brunk20796122015-07-21 17:51:54 -07001154 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001155 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001156 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001157
Ruben Brunkf967a542014-04-28 16:31:11 -07001158 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001159 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001160 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001161
Ruben Brunkf967a542014-04-28 16:31:11 -07001162 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001163 reinterpret_cast<jlong>(context.get()));
1164}
1165
Ruben Brunkf967a542014-04-28 16:31:11 -07001166static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
1167 ALOGV("%s:", __FUNCTION__);
1168
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001169 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1170 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001171
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001172 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1173 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1174 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001175
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001176 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1177 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1178 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001179
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001180 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1181 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1182 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001183}
1184
1185static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001186 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001187 ALOGV("%s:", __FUNCTION__);
1188 CameraMetadata characteristics;
1189 CameraMetadata results;
1190 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1191 jniThrowException(env, "java/lang/AssertionError",
1192 "No native metadata defined for camera characteristics.");
1193 return;
1194 }
1195 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1196 jniThrowException(env, "java/lang/AssertionError",
1197 "No native metadata defined for capture results.");
1198 return;
1199 }
1200
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001201 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001202
1203 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1204
1205 size_t len = strlen(captureTime) + 1;
1206 if (len != NativeContext::DATETIME_COUNT) {
1207 jniThrowException(env, "java/lang/IllegalArgumentException",
1208 "Formatted capture time string length is not required 20 characters");
1209 return;
1210 }
1211
1212 nativeContext->setCaptureTime(String8(captureTime));
1213
1214 DngCreator_setNativeContext(env, thiz, nativeContext);
1215}
1216
1217static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1218 uint32_t imageHeight) {
1219
1220 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1221
1222 if (nativeContext == nullptr) {
1223 jniThrowException(env, "java/lang/AssertionError",
1224 "No native context, must call init before other operations.");
1225 return nullptr;
1226 }
1227
1228 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1229 CameraMetadata results = *(nativeContext->getResult());
1230
1231 sp<TiffWriter> writer = new TiffWriter();
1232
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001233 uint32_t preXMin = 0;
1234 uint32_t preYMin = 0;
Ruben Brunk20796122015-07-21 17:51:54 -07001235 uint32_t preWidth = 0;
1236 uint32_t preHeight = 0;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001237 uint8_t colorFilter = 0;
1238 bool isBayer = true;
Ruben Brunk20796122015-07-21 17:51:54 -07001239 {
1240 // Check dimensions
1241 camera_metadata_entry entry =
1242 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1243 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001244 preXMin = static_cast<uint32_t>(entry.data.i32[0]);
1245 preYMin = static_cast<uint32_t>(entry.data.i32[1]);
Ruben Brunk20796122015-07-21 17:51:54 -07001246 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1247 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1248
1249 camera_metadata_entry pixelArrayEntry =
1250 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1251 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1252 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1253
1254 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001255 (imageWidth == pixWidth && imageHeight == pixHeight))) {
Ruben Brunk20796122015-07-21 17:51:54 -07001256 jniThrowException(env, "java/lang/AssertionError",
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001257 "Height and width of image buffer did not match height and width of"
Ruben Brunk20796122015-07-21 17:51:54 -07001258 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1259 return nullptr;
1260 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001261
1262 camera_metadata_entry colorFilterEntry =
1263 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1264 colorFilter = colorFilterEntry.data.u8[0];
1265 camera_metadata_entry capabilitiesEntry =
1266 characteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
1267 size_t capsCount = capabilitiesEntry.count;
1268 uint8_t* caps = capabilitiesEntry.data.u8;
1269 if (std::find(caps, caps+capsCount, ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME)
1270 != caps+capsCount) {
1271 isBayer = false;
1272 } else if (colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO ||
1273 colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
1274 jniThrowException(env, "java/lang/AssertionError",
1275 "A camera device with MONO/NIR color filter must have MONOCHROME capability.");
1276 return nullptr;
1277 }
Ruben Brunk20796122015-07-21 17:51:54 -07001278 }
1279
Ruben Brunkf967a542014-04-28 16:31:11 -07001280 writer->addIfd(TIFF_IFD_0);
1281
1282 status_t err = OK;
1283
1284 const uint32_t samplesPerPixel = 1;
1285 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001286
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001287 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_NONE;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001288 uint8_t cfaPlaneColor[3] = {0, 1, 2};
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001289 camera_metadata_entry cfaEntry =
1290 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
1291 BAIL_IF_EMPTY_RET_NULL_SP(cfaEntry, env, TAG_CFAPATTERN, writer);
1292 uint8_t cfaEnum = cfaEntry.data.u8[0];
Ruben Brunkf967a542014-04-28 16:31:11 -07001293
1294 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001295 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001296
1297 // Setup main image tags
1298
Ruben Brunkf967a542014-04-28 16:31:11 -07001299 {
1300 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001301 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001302 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1303 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001304 }
1305
1306 {
1307 // Set subfiletype
1308 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001309 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1310 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001311 }
1312
1313 {
1314 // Set bits per sample
1315 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001316 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001317 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001318 }
1319
1320 {
1321 // Set compression
1322 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001323 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1324 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001325 }
1326
1327 {
1328 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001329 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1330 env, TAG_IMAGEWIDTH, writer);
1331 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1332 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001333 }
1334
1335 {
1336 // Set photometric interpretation
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001337 uint16_t interpretation = isBayer ? 32803 /* CFA */ :
1338 34892; /* Linear Raw */;
Ruben Brunk20796122015-07-21 17:51:54 -07001339 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1340 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001341 }
1342
1343 {
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001344 uint16_t repeatDim[2] = {2, 2};
1345 if (!isBayer) {
1346 repeatDim[0] = repeatDim[1] = 1;
1347 }
1348 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1349 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
1350
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001351 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001352 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001353 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1354 uint32_t blackLevelRational[8] = {0};
1355 if (entry.count != 0) {
1356 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1357 for (size_t i = 0; i < entry.count; i++) {
1358 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1359 blackLevelRational[i * 2 + 1] = 100;
1360 }
1361 } else {
1362 // Fall back to static black level which is guaranteed
1363 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1364 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1365 for (size_t i = 0; i < entry.count; i++) {
1366 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1367 blackLevelRational[i * 2 + 1] = 1;
1368 }
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001369 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001370 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, repeatDim[0]*repeatDim[1],
1371 blackLevelRational, TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001372 }
1373
1374 {
1375 // Set samples per pixel
1376 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001377 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001378 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001379 }
1380
1381 {
1382 // Set planar configuration
1383 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001384 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1385 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001386 }
1387
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001388 // All CFA pattern tags are not necessary for monochrome cameras.
1389 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001390 // Set CFA pattern dimensions
1391 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001392 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1393 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001394
Ruben Brunkf967a542014-04-28 16:31:11 -07001395 // Set CFA pattern
Ruben Brunkd70132c2014-08-22 16:24:49 -07001396 const int cfaLength = 4;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001397 uint8_t cfa[cfaLength];
1398 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1399 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1400 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001401 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001402
Ruben Brunk20796122015-07-21 17:51:54 -07001403 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1404 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001405
1406 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001407
Ruben Brunkf967a542014-04-28 16:31:11 -07001408 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001409 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1410 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001411
Ruben Brunkf967a542014-04-28 16:31:11 -07001412 // Set CFA layout
1413 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001414 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001415 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001416 }
1417
1418 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001419 // image description
1420 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001421 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1422 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001423 }
1424
1425 {
1426 // make
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001427 // Use "" to represent unknown make as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001428 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1429 uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001430
Ruben Brunk20796122015-07-21 17:51:54 -07001431 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001432 reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE,
1433 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001434 }
1435
1436 {
1437 // model
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001438 // Use "" to represent unknown model as suggested in TIFF/EP spec.
Tom Cherry93099e42017-10-11 13:44:21 -07001439 std::string model = GetProperty("ro.product.model", "");
1440 uint32_t count = static_cast<uint32_t>(model.size()) + 1;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001441
Ruben Brunk20796122015-07-21 17:51:54 -07001442 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001443 reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL,
1444 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001445 }
1446
1447 {
1448 // x resolution
1449 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001450 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001451 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001452
1453 // y resolution
1454 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001455 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001456 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001457
1458 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001459 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001460 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001461 }
1462
1463 {
1464 // software
Tom Cherry93099e42017-10-11 13:44:21 -07001465 std::string software = GetProperty("ro.build.fingerprint", "");
1466 uint32_t count = static_cast<uint32_t>(software.size()) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001467 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
Tom Cherry93099e42017-10-11 13:44:21 -07001468 reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE,
1469 writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001470 }
1471
Ruben Brunk20796122015-07-21 17:51:54 -07001472 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001473 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001474 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001475
Ruben Brunk20796122015-07-21 17:51:54 -07001476 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1477 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001478 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1479 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001480 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001481 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001482
1483 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001484 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1485 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001486 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1487 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001488 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001489 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001490 }
1491
1492 {
1493 // TIFF/EP standard id
1494 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001495 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001496 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001497 }
1498
1499 {
1500 // copyright
1501 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001502 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001503 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001504 }
1505
1506 {
1507 // exposure time
1508 camera_metadata_entry entry =
1509 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001510 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001511
1512 int64_t exposureTime = *(entry.data.i64);
1513
1514 if (exposureTime < 0) {
1515 // Should be unreachable
1516 jniThrowException(env, "java/lang/IllegalArgumentException",
1517 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001518 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001519 }
1520
1521 // Ensure exposure time doesn't overflow (for exposures > 4s)
1522 uint32_t denominator = 1000000000;
1523 while (exposureTime > UINT32_MAX) {
1524 exposureTime >>= 1;
1525 denominator >>= 1;
1526 if (denominator == 0) {
1527 // Should be unreachable
1528 jniThrowException(env, "java/lang/IllegalArgumentException",
1529 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001530 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001531 }
1532 }
1533
1534 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001535 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001536 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001537
1538 }
1539
1540 {
1541 // ISO speed ratings
1542 camera_metadata_entry entry =
1543 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001544 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001545
1546 int32_t tempIso = *(entry.data.i32);
1547 if (tempIso < 0) {
1548 jniThrowException(env, "java/lang/IllegalArgumentException",
1549 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001550 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001551 }
1552
1553 if (tempIso > UINT16_MAX) {
1554 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1555 tempIso = UINT16_MAX;
1556 }
1557
1558 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001559 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001560 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001561 }
1562
1563 {
Emilian Peev7ca13712017-04-05 15:42:22 +01001564 // Baseline exposure
1565 camera_metadata_entry entry =
1566 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
1567 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
1568
1569 // post RAW gain should be boostValue / 100
1570 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
1571 // Baseline exposure should be in EV units so log2(gain) =
1572 // log10(gain)/log10(2)
1573 double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
1574 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
1575 100 };
1576 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
1577 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
1578 }
1579
1580 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001581 // focal length
1582 camera_metadata_entry entry =
1583 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001584 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001585
1586 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001587 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001588 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001589 }
1590
1591 {
1592 // f number
1593 camera_metadata_entry entry =
1594 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001595 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001596
1597 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001598 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001599 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001600 }
1601
1602 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001603 // Set DNG version information
1604 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001605 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001606 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001607
1608 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001609 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1610 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001611 }
1612
1613 {
1614 // Set whitelevel
1615 camera_metadata_entry entry =
1616 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001617 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001618 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001619 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1620 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001621 }
1622
1623 {
1624 // Set default scale
1625 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001626 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1627 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001628 }
1629
1630 bool singleIlluminant = false;
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001631 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001632 // Set calibration illuminants
1633 camera_metadata_entry entry1 =
1634 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001635 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001636 camera_metadata_entry entry2 =
1637 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1638 if (entry2.count == 0) {
1639 singleIlluminant = true;
1640 }
1641 uint16_t ref1 = entry1.data.u8[0];
1642
Ruben Brunk20796122015-07-21 17:51:54 -07001643 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001644 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001645
1646 if (!singleIlluminant) {
1647 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001648 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001649 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001650 }
1651 }
1652
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001653 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001654 // Set color transforms
1655 camera_metadata_entry entry1 =
1656 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001657 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001658
1659 int32_t colorTransform1[entry1.count * 2];
1660
1661 size_t ctr = 0;
1662 for(size_t i = 0; i < entry1.count; ++i) {
1663 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1664 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1665 }
1666
Ruben Brunk20796122015-07-21 17:51:54 -07001667 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1668 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001669
1670 if (!singleIlluminant) {
1671 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001672 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001673 int32_t colorTransform2[entry2.count * 2];
1674
1675 ctr = 0;
1676 for(size_t i = 0; i < entry2.count; ++i) {
1677 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1678 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1679 }
1680
Ruben Brunk20796122015-07-21 17:51:54 -07001681 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1682 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001683 }
1684 }
1685
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001686 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001687 // Set calibration transforms
1688 camera_metadata_entry entry1 =
1689 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001690 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001691
1692 int32_t calibrationTransform1[entry1.count * 2];
1693
1694 size_t ctr = 0;
1695 for(size_t i = 0; i < entry1.count; ++i) {
1696 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1697 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1698 }
1699
Ruben Brunk20796122015-07-21 17:51:54 -07001700 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001701 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001702
1703 if (!singleIlluminant) {
1704 camera_metadata_entry entry2 =
1705 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001706 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001707 int32_t calibrationTransform2[entry2.count * 2];
1708
1709 ctr = 0;
1710 for(size_t i = 0; i < entry2.count; ++i) {
1711 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1712 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1713 }
1714
Ruben Brunk20796122015-07-21 17:51:54 -07001715 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001716 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001717 }
1718 }
1719
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001720 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001721 // Set forward transforms
1722 camera_metadata_entry entry1 =
1723 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001724 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001725
1726 int32_t forwardTransform1[entry1.count * 2];
1727
1728 size_t ctr = 0;
1729 for(size_t i = 0; i < entry1.count; ++i) {
1730 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1731 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1732 }
1733
Ruben Brunk20796122015-07-21 17:51:54 -07001734 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1735 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001736
1737 if (!singleIlluminant) {
1738 camera_metadata_entry entry2 =
1739 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001740 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001741 int32_t forwardTransform2[entry2.count * 2];
1742
1743 ctr = 0;
1744 for(size_t i = 0; i < entry2.count; ++i) {
1745 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1746 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1747 }
1748
Ruben Brunk20796122015-07-21 17:51:54 -07001749 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1750 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001751 }
1752 }
1753
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001754 if (isBayer) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001755 // Set camera neutral
1756 camera_metadata_entry entry =
1757 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001758 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001759 uint32_t cameraNeutral[entry.count * 2];
1760
1761 size_t ctr = 0;
1762 for(size_t i = 0; i < entry.count; ++i) {
1763 cameraNeutral[ctr++] =
1764 static_cast<uint32_t>(entry.data.r[i].numerator);
1765 cameraNeutral[ctr++] =
1766 static_cast<uint32_t>(entry.data.r[i].denominator);
1767 }
1768
Ruben Brunk20796122015-07-21 17:51:54 -07001769 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001770 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001771 }
1772
Ruben Brunkf967a542014-04-28 16:31:11 -07001773
1774 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001775 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001776 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001777 return nullptr;
1778 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001779 camera_metadata_entry entry =
1780 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001781 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001782 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1783 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1784 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1785 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001786
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001787 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1788 // relative to the pixel array.
1789 if (imageWidth == width && imageHeight == height) {
1790 xmin = 0;
1791 ymin = 0;
1792 }
1793
Ruben Brunk20796122015-07-21 17:51:54 -07001794 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1795 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1796 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001797 }
1798
1799 {
1800 // Setup unique camera model tag
Tom Cherry93099e42017-10-11 13:44:21 -07001801 std::string model = GetProperty("ro.product.model", "");
1802 std::string manufacturer = GetProperty("ro.product.manufacturer", "");
1803 std::string brand = GetProperty("ro.product.brand", "");
Ruben Brunkf967a542014-04-28 16:31:11 -07001804
Tom Cherry93099e42017-10-11 13:44:21 -07001805 String8 cameraModel(model.c_str());
Ruben Brunkf967a542014-04-28 16:31:11 -07001806 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001807 cameraModel += manufacturer.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001808 cameraModel += "-";
Tom Cherry93099e42017-10-11 13:44:21 -07001809 cameraModel += brand.c_str();
Ruben Brunkf967a542014-04-28 16:31:11 -07001810
Ruben Brunk20796122015-07-21 17:51:54 -07001811 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001812 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001813 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001814 }
1815
1816 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001817 // Setup sensor noise model
1818 camera_metadata_entry entry =
1819 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1820
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001821 const status_t numPlaneColors = isBayer ? 3 : 1;
1822 const status_t numCfaChannels = isBayer ? 4 : 1;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001823
1824 uint8_t cfaOut[numCfaChannels];
1825 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1826 jniThrowException(env, "java/lang/IllegalArgumentException",
1827 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001828 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001829 }
1830
1831 double noiseProfile[numPlaneColors * 2];
1832
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001833 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001834 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001835 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1836 "in characteristics, no noise profile tag written...",
1837 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001838 } else {
1839 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1840 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1841
Ruben Brunk20796122015-07-21 17:51:54 -07001842 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1843 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1844 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001845 } else {
1846 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1847 " tag written...", __FUNCTION__);
1848 }
1849 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001850 } else {
1851 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1852 __FUNCTION__);
1853 }
1854 }
1855
1856 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001857 // Set up opcode List 2
1858 OpcodeListBuilder builder;
1859 status_t err = OK;
1860
1861 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001862 camera_metadata_entry entry1 =
1863 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001864
1865 uint32_t lsmWidth = 0;
1866 uint32_t lsmHeight = 0;
1867
1868 if (entry1.count != 0) {
1869 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1870 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1871 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001872
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001873 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001874
Ruben Brunk20796122015-07-21 17:51:54 -07001875 camera_metadata_entry entry =
1876 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1877 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1878 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1879 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1880 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1881 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001882 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001883 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001884 err = builder.addGainMapsForMetadata(lsmWidth,
1885 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001886 0,
1887 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001888 height,
1889 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001890 opcodeCfaLayout,
1891 entry2.data.f);
1892 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001893 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1894 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001895 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001896 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001897 }
1898
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001899 // Hot pixel map is specific to bayer camera per DNG spec.
1900 if (isBayer) {
1901 // Set up bad pixel correction list
1902 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001903
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001904 if ((entry3.count % 2) != 0) {
1905 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1906 __FUNCTION__);
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001907 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1908 return nullptr;
1909 }
Shuzhen Wanga8d36032018-10-15 12:01:53 -07001910
1911 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1912 std::vector<uint32_t> v;
1913 for (size_t i = 0; i < entry3.count; i += 2) {
1914 int32_t x = entry3.data.i32[i];
1915 int32_t y = entry3.data.i32[i + 1];
1916 x -= static_cast<int32_t>(xmin);
1917 y -= static_cast<int32_t>(ymin);
1918 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1919 static_cast<uint32_t>(y) >= height) {
1920 continue;
1921 }
1922 v.push_back(x);
1923 v.push_back(y);
1924 }
1925 const uint32_t* badPixels = &v[0];
1926 uint32_t badPixelCount = v.size();
1927
1928 if (badPixelCount > 0) {
1929 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1930
1931 if (err != OK) {
1932 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1933 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1934 return nullptr;
1935 }
1936 }
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001937 }
1938
Sam Hasinoff58398342018-08-01 18:24:59 -07001939 if (builder.getCount() > 0) {
1940 size_t listSize = builder.getSize();
1941 uint8_t opcodeListBuf[listSize];
1942 err = builder.buildOpList(opcodeListBuf);
1943 if (err == OK) {
1944 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize,
1945 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1946 } else {
1947 ALOGE("%s: Could not build list of opcodes for lens shading map and bad pixel "
1948 "correction.", __FUNCTION__);
1949 jniThrowRuntimeException(env, "failed to construct opcode list for lens shading "
1950 "map and bad pixel correction");
1951 return nullptr;
1952 }
Ruben Brunkfe816622015-06-16 23:03:09 -07001953 }
1954 }
1955
1956 {
1957 // Set up opcode List 3
1958 OpcodeListBuilder builder;
1959 status_t err = OK;
1960
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001961 // Set up rectilinear distortion correction
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001962 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 -07001963 bool gotDistortion = false;
1964
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001965 camera_metadata_entry entry4 =
1966 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1967
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001968 if (entry4.count == 5) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001969 float cx = entry4.data.f[/*c_x*/2];
1970 float cy = entry4.data.f[/*c_y*/3];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07001971 // Assuming f_x = f_y, or at least close enough.
1972 // Also assuming s = 0, or at least close enough.
1973 float f = entry4.data.f[/*f_x*/0];
1974
1975 camera_metadata_entry entry3 =
1976 results.find(ANDROID_LENS_DISTORTION);
1977 if (entry3.count == 5) {
1978 gotDistortion = true;
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001979
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001980 // Scale the distortion coefficients to create a zoom in warpped image so that all
1981 // pixels are drawn within input image.
1982 for (size_t i = 0; i < entry3.count; i++) {
1983 distortion[i+1] = entry3.data.f[i];
1984 }
1985
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001986 if (preWidth == imageWidth && preHeight == imageHeight) {
1987 normalizeLensDistortion(distortion, cx, cy, f, preWidth, preHeight);
Yin-Chia Yeh0cae8392019-04-11 14:41:31 -07001988 } else {
1989 // image size == pixel array size (contains optical black pixels)
1990 // cx/cy is defined in preCorrArray so adding the offset
1991 // Also changes default xmin/ymin so that pixels are only
1992 // sampled within preCorrection array
1993 normalizeLensDistortion(
1994 distortion, cx + preXMin, cy + preYMin, f, preWidth, preHeight,
1995 preXMin, preYMin);
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07001996 }
1997
Shuzhen Wang461f3cf2019-04-19 12:19:01 -07001998 float m_x = std::fmaxf(preWidth - cx, cx);
1999 float m_y = std::fmaxf(preHeight - cy, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07002000 float m_sq = m_x*m_x + m_y*m_y;
2001 float m = sqrtf(m_sq); // distance to farthest corner from optical center
2002 float f_sq = f * f;
2003 // Conversion factors from Camera2 K factors for new LENS_DISTORTION field
2004 // to DNG spec.
2005 //
2006 // Camera2 / OpenCV assume distortion is applied in a space where focal length
2007 // is factored out, while DNG assumes a normalized space where the distance
2008 // from optical center to the farthest corner is 1.
2009 // Scale from camera2 to DNG spec accordingly.
2010 // distortion[0] is always 1 with the new LENS_DISTORTION field.
2011 const double convCoeff[5] = {
2012 m_sq / f_sq,
2013 pow(m_sq, 2) / pow(f_sq, 2),
2014 pow(m_sq, 3) / pow(f_sq, 3),
2015 m / f,
2016 m / f
2017 };
2018 for (size_t i = 0; i < entry3.count; i++) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07002019 distortion[i+1] *= convCoeff[i];
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07002020 }
2021 } else {
2022 entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION);
2023 if (entry3.count == 6) {
2024 gotDistortion = true;
2025 // Conversion factors from Camera2 K factors to DNG spec. K factors:
2026 //
2027 // Note: these are necessary because our unit system assumes a
2028 // normalized max radius of sqrt(2), whereas the DNG spec's
2029 // WarpRectilinear opcode assumes a normalized max radius of 1.
2030 // Thus, each K coefficient must include the domain scaling
2031 // factor (the DNG domain is scaled by sqrt(2) to emulate the
2032 // domain used by the Camera2 specification).
2033 const double convCoeff[6] = {
2034 sqrt(2),
2035 2 * sqrt(2),
2036 4 * sqrt(2),
2037 8 * sqrt(2),
2038 2,
2039 2
2040 };
2041 for (size_t i = 0; i < entry3.count; i++) {
2042 distortion[i] = entry3.data.f[i] * convCoeff[i];
2043 }
2044 }
2045 }
2046 if (gotDistortion) {
Yin-Chia Yehf52c21d2019-04-03 12:43:09 -07002047 err = builder.addWarpRectilinearForMetadata(
2048 distortion.data(), preWidth, preHeight, cx, cy);
Eino-Ville Talvalaa7beefc2018-04-12 14:47:31 -07002049 if (err != OK) {
2050 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
2051 jniThrowRuntimeException(env, "failed to add distortion correction.");
2052 return nullptr;
2053 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07002054 }
2055 }
2056
Sam Hasinoff58398342018-08-01 18:24:59 -07002057 if (builder.getCount() > 0) {
2058 size_t listSize = builder.getSize();
2059 uint8_t opcodeListBuf[listSize];
2060 err = builder.buildOpList(opcodeListBuf);
2061 if (err == OK) {
2062 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize,
2063 opcodeListBuf, TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
2064 } else {
2065 ALOGE("%s: Could not build list of opcodes for distortion correction.",
2066 __FUNCTION__);
2067 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
2068 " correction");
2069 return nullptr;
2070 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002071 }
2072 }
2073
Ruben Brunk20796122015-07-21 17:51:54 -07002074 {
2075 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08002076 // Note: There's only one orientation field for the whole file, in IFD0
2077 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07002078 uint16_t orientation = nativeContext->getOrientation();
2079 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
2080 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07002081
Ruben Brunk47e91f22014-05-28 18:38:42 -07002082 }
2083
Ruben Brunk20796122015-07-21 17:51:54 -07002084 if (nativeContext->hasDescription()){
2085 // Set Description
2086 String8 description = nativeContext->getDescription();
2087 size_t len = description.bytes() + 1;
2088 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
2089 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
2090 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
2091 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002092 }
2093 }
2094
Ruben Brunk20796122015-07-21 17:51:54 -07002095 if (nativeContext->hasGpsData()) {
2096 // Set GPS tags
2097 GpsData gpsData = nativeContext->getGpsData();
2098 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
2099 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
2100 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
2101 TIFF_IFD_0);
2102 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
2103 return nullptr;
2104 }
2105 }
2106
2107 {
2108 uint8_t version[] = {2, 3, 0, 0};
2109 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
2110 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
2111 }
2112
2113 {
2114 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
2115 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
2116 TAG_GPSLATITUDEREF, writer);
2117 }
2118
2119 {
2120 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
2121 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
2122 TAG_GPSLONGITUDEREF, writer);
2123 }
2124
2125 {
2126 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
2127 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
2128 }
2129
2130 {
2131 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
2132 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
2133 }
2134
2135 {
2136 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
2137 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
2138 }
2139
2140 {
2141 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
2142 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
2143 TAG_GPSDATESTAMP, writer);
2144 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002145 }
2146
Ruben Brunk47e91f22014-05-28 18:38:42 -07002147
Ruben Brunk20796122015-07-21 17:51:54 -07002148 if (nativeContext->hasThumbnail()) {
2149 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
2150 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
2151 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
2152 TIFF_IFD_0);
2153 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
2154 return nullptr;
2155 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002156 }
2157
2158 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002159 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07002160 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002161 tagsToMove.add(TAG_BITSPERSAMPLE);
2162 tagsToMove.add(TAG_COMPRESSION);
2163 tagsToMove.add(TAG_IMAGEWIDTH);
2164 tagsToMove.add(TAG_IMAGELENGTH);
2165 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
2166 tagsToMove.add(TAG_BLACKLEVEL);
2167 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
2168 tagsToMove.add(TAG_SAMPLESPERPIXEL);
2169 tagsToMove.add(TAG_PLANARCONFIGURATION);
Shuzhen Wanga8d36032018-10-15 12:01:53 -07002170 if (isBayer) {
2171 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
2172 tagsToMove.add(TAG_CFAPATTERN);
2173 tagsToMove.add(TAG_CFAPLANECOLOR);
2174 tagsToMove.add(TAG_CFALAYOUT);
2175 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002176 tagsToMove.add(TAG_XRESOLUTION);
2177 tagsToMove.add(TAG_YRESOLUTION);
2178 tagsToMove.add(TAG_RESOLUTIONUNIT);
2179 tagsToMove.add(TAG_WHITELEVEL);
2180 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002181 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
2182 tagsToMove.add(TAG_DEFAULTCROPSIZE);
Emilian Peeva7503b92018-08-13 12:46:05 +01002183
2184 if (nullptr != writer->getEntry(TAG_OPCODELIST2, TIFF_IFD_0).get()) {
2185 tagsToMove.add(TAG_OPCODELIST2);
2186 }
2187
2188 if (nullptr != writer->getEntry(TAG_OPCODELIST3, TIFF_IFD_0).get()) {
2189 tagsToMove.add(TAG_OPCODELIST3);
2190 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002191
2192 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
2193 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07002194 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002195 }
2196
Ruben Brunk20796122015-07-21 17:51:54 -07002197 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07002198
Ruben Brunk20796122015-07-21 17:51:54 -07002199 {
2200 // Set photometric interpretation
2201 uint16_t interpretation = 2; // RGB
2202 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
2203 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002204 }
Ruben Brunk20796122015-07-21 17:51:54 -07002205
2206 {
2207 // Set planar configuration
2208 uint16_t config = 1; // Chunky
2209 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
2210 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
2211 }
2212
2213 {
2214 // Set samples per pixel
2215 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
2216 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
2217 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
2218 }
2219
2220 {
2221 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08002222 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
2223 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
2224 BAIL_IF_INVALID_RET_NULL_SP(
2225 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07002226 env, TAG_BITSPERSAMPLE, writer);
2227 }
2228
2229 {
2230 // Set subfiletype
2231 uint32_t subfileType = 1; // Thumbnail image
2232 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
2233 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
2234 }
2235
2236 {
2237 // Set compression
2238 uint16_t compression = 1; // None
2239 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
2240 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
2241 }
2242
2243 {
2244 // Set dimensions
2245 uint32_t uWidth = nativeContext->getThumbnailWidth();
2246 uint32_t uHeight = nativeContext->getThumbnailHeight();
2247 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
2248 env, TAG_IMAGEWIDTH, writer);
2249 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
2250 env, TAG_IMAGELENGTH, writer);
2251 }
2252
2253 {
2254 // x resolution
2255 uint32_t xres[] = { 72, 1 }; // default 72 ppi
2256 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
2257 env, TAG_XRESOLUTION, writer);
2258
2259 // y resolution
2260 uint32_t yres[] = { 72, 1 }; // default 72 ppi
2261 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
2262 env, TAG_YRESOLUTION, writer);
2263
2264 uint16_t unit = 2; // inches
2265 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
2266 env, TAG_RESOLUTIONUNIT, writer);
2267 }
2268 }
2269
2270 if (writer->addStrip(TIFF_IFD_0) != OK) {
2271 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
2272 jniThrowException(env, "java/lang/IllegalStateException",
2273 "Failed to setup thumbnail strip tags.");
2274 return nullptr;
2275 }
2276
2277 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002278 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
2279 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
2280 jniThrowException(env, "java/lang/IllegalStateException",
2281 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07002282 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002283 }
2284 }
Ruben Brunk20796122015-07-21 17:51:54 -07002285 return writer;
2286}
2287
2288static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2289 ALOGV("%s:", __FUNCTION__);
2290 DngCreator_setNativeContext(env, thiz, nullptr);
2291}
2292
2293static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2294 ALOGV("%s:", __FUNCTION__);
2295
2296 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2297 if (context == nullptr) {
2298 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2299 jniThrowException(env, "java/lang/AssertionError",
2300 "setOrientation called with uninitialized DngCreator");
2301 return;
2302 }
2303
2304 uint16_t orientation = static_cast<uint16_t>(orient);
2305 context->setOrientation(orientation);
2306}
2307
2308static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2309 ALOGV("%s:", __FUNCTION__);
2310
2311 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2312 if (context == nullptr) {
2313 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2314 jniThrowException(env, "java/lang/AssertionError",
2315 "setDescription called with uninitialized DngCreator");
2316 return;
2317 }
2318
2319 const char* desc = env->GetStringUTFChars(description, nullptr);
2320 context->setDescription(String8(desc));
2321 env->ReleaseStringUTFChars(description, desc);
2322}
2323
2324static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2325 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2326 ALOGV("%s:", __FUNCTION__);
2327
2328 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2329 if (context == nullptr) {
2330 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2331 jniThrowException(env, "java/lang/AssertionError",
2332 "setGpsTags called with uninitialized DngCreator");
2333 return;
2334 }
2335
2336 GpsData data;
2337
2338 jsize latLen = env->GetArrayLength(latTag);
2339 jsize longLen = env->GetArrayLength(longTag);
2340 jsize timeLen = env->GetArrayLength(timeTag);
2341 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2342 jniThrowException(env, "java/lang/IllegalArgumentException",
2343 "invalid latitude tag length");
2344 return;
2345 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2346 jniThrowException(env, "java/lang/IllegalArgumentException",
2347 "invalid longitude tag length");
2348 return;
2349 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2350 jniThrowException(env, "java/lang/IllegalArgumentException",
2351 "invalid time tag length");
2352 return;
2353 }
2354
2355 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2356 reinterpret_cast<jint*>(&data.mLatitude));
2357 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2358 reinterpret_cast<jint*>(&data.mLongitude));
2359 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2360 reinterpret_cast<jint*>(&data.mTimestamp));
2361
2362
2363 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2364 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2365 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2366 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2367 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2368 reinterpret_cast<char*>(&data.mDate));
2369 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2370
2371 context->setGpsData(data);
2372}
2373
2374static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2375 jint height) {
2376 ALOGV("%s:", __FUNCTION__);
2377
2378 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2379 if (context == nullptr) {
2380 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2381 jniThrowException(env, "java/lang/AssertionError",
2382 "setThumbnail called with uninitialized DngCreator");
2383 return;
2384 }
2385
2386 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2387 jlong capacity = env->GetDirectBufferCapacity(buffer);
2388 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2389 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2390 "Invalid size %d for thumbnail, expected size was %d",
2391 capacity, fullSize);
2392 return;
2393 }
2394
2395 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2396 if (pixelBytes == nullptr) {
2397 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2398 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2399 return;
2400 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002401
2402 if (!context->setThumbnail(pixelBytes, width, height)) {
2403 jniThrowException(env, "java/lang/IllegalStateException",
2404 "Failed to set thumbnail.");
2405 return;
2406 }
2407}
2408
2409// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002410static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002411 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2412 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002413 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002414 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2415 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2416 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002417 uint32_t rStride = static_cast<uint32_t>(rowStride);
2418 uint32_t pStride = static_cast<uint32_t>(pixStride);
2419 uint32_t uWidth = static_cast<uint32_t>(width);
2420 uint32_t uHeight = static_cast<uint32_t>(height);
2421 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002422
2423 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2424 if(env->ExceptionCheck()) {
2425 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2426 return;
2427 }
2428
Ruben Brunk47e91f22014-05-28 18:38:42 -07002429 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002430 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002431 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2432 jniThrowException(env, "java/lang/AssertionError",
2433 "Write called with uninitialized DngCreator");
2434 return;
2435 }
Ruben Brunk20796122015-07-21 17:51:54 -07002436 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002437
Ruben Brunk20796122015-07-21 17:51:54 -07002438 if (writer.get() == nullptr) {
2439 return;
2440 }
2441
2442 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002443 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002444 return;
2445 }
2446
Ruben Brunk47e91f22014-05-28 18:38:42 -07002447 sp<JniInputByteBuffer> inBuf;
2448 Vector<StripSource*> sources;
2449 sp<DirectStripSource> thumbnailSource;
2450 uint32_t targetIfd = TIFF_IFD_0;
2451
2452 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2453
2454 if (hasThumbnail) {
2455 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2456 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2457 uint32_t thumbWidth = context->getThumbnailWidth();
2458 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2459 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2460 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2461 SAMPLES_PER_RGB_PIXEL);
2462 sources.add(thumbnailSource.get());
2463 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002464 }
2465
Ruben Brunk47e91f22014-05-28 18:38:42 -07002466 if (isDirect) {
2467 size_t fullSize = rStride * uHeight;
2468 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2469 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2470 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2471 "Invalid size %d for Image, size given in metadata is %d at current stride",
2472 capacity, fullSize);
2473 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002474 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002475
Ruben Brunk47e91f22014-05-28 18:38:42 -07002476 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002477 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002478 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2479 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2480 return;
2481 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002482
Ruben Brunk47e91f22014-05-28 18:38:42 -07002483 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2484 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2485 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2486 sources.add(&stripSource);
2487
2488 status_t ret = OK;
2489 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2490 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002491 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002492 jniThrowExceptionFmt(env, "java/io/IOException",
2493 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002494 }
2495 return;
2496 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002497 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002498 inBuf = new JniInputByteBuffer(env, inBuffer);
2499
2500 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2501 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2502 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2503 sources.add(&stripSource);
2504
2505 status_t ret = OK;
2506 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2507 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2508 if (!env->ExceptionCheck()) {
2509 jniThrowExceptionFmt(env, "java/io/IOException",
2510 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002511 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002512 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002513 }
2514 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002515}
2516
2517static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002518 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002519 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002520
2521 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2522 uint32_t pixStride = BYTES_PER_SAMPLE;
2523 uint32_t uWidth = static_cast<uint32_t>(width);
2524 uint32_t uHeight = static_cast<uint32_t>(height);
2525 uint64_t uOffset = static_cast<uint32_t>(offset);
2526
Dan Albert46d84442014-11-18 16:07:51 -08002527 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2528 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2529 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002530
2531 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002532 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002533 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2534 return;
2535 }
2536
Ruben Brunk47e91f22014-05-28 18:38:42 -07002537 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002538 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002539 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2540 jniThrowException(env, "java/lang/AssertionError",
2541 "Write called with uninitialized DngCreator");
2542 return;
2543 }
Ruben Brunk20796122015-07-21 17:51:54 -07002544 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002545
Ruben Brunk20796122015-07-21 17:51:54 -07002546 if (writer.get() == nullptr) {
2547 return;
2548 }
2549
2550 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002551 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002552 return;
2553 }
2554
2555 sp<DirectStripSource> thumbnailSource;
2556 uint32_t targetIfd = TIFF_IFD_0;
2557 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2558 Vector<StripSource*> sources;
2559
2560 if (hasThumbnail) {
2561 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2562 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2563 uint32_t width = context->getThumbnailWidth();
2564 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2565 width, context->getThumbnailHeight(), bytesPerPixel,
2566 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2567 SAMPLES_PER_RGB_PIXEL);
2568 sources.add(thumbnailSource.get());
2569 targetIfd = TIFF_IFD_SUB1;
2570 }
2571
2572 sp<JniInputStream> in = new JniInputStream(env, inStream);
2573
2574 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2575 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2576 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2577 sources.add(&stripSource);
2578
2579 status_t ret = OK;
2580 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2581 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2582 if (!env->ExceptionCheck()) {
2583 jniThrowExceptionFmt(env, "java/io/IOException",
2584 "Encountered error %d while writing file.", ret);
2585 }
2586 return;
2587 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002588}
2589
2590} /*extern "C" */
2591
Daniel Micay76f6a862015-09-19 17:31:01 -04002592static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002593 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2594 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002595 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2596 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002597 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2598 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002599 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2600 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2601 (void*) DngCreator_nativeSetGpsTags},
2602 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2603 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002604 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002605 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002606 (void*) DngCreator_nativeWriteInputStream},
2607};
2608
Ruben Brunkb6079002014-05-22 12:33:54 -07002609int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002610 return RegisterMethodsOrDie(env,
2611 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002612}