blob: d80d8f2621897fed1f89c237ff2a0049f5196563 [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -080017//#define LOG_NDEBUG 0
Ruben Brunkf967a542014-04-28 16:31:11 -070018#define LOG_TAG "DngCreator_JNI"
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <string.h>
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070021#include <algorithm>
22#include <memory>
Ruben Brunk9ce22a02015-08-03 12:40:11 -070023#include <vector>
Dan Albert46d84442014-11-18 16:07:51 -080024
25#include <utils/Log.h>
26#include <utils/Errors.h>
27#include <utils/StrongPointer.h>
28#include <utils/RefBase.h>
29#include <utils/Vector.h>
Ruben Brunk20796122015-07-21 17:51:54 -070030#include <utils/String8.h>
Dan Albert46d84442014-11-18 16:07:51 -080031#include <cutils/properties.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070032#include <system/camera_metadata.h>
33#include <camera/CameraMetadata.h>
34#include <img_utils/DngUtils.h>
35#include <img_utils/TagDefinitions.h>
36#include <img_utils/TiffIfd.h>
37#include <img_utils/TiffWriter.h>
38#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070039#include <img_utils/Input.h>
40#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070041
Andreas Gampeed6b9df2014-11-20 22:02:20 -080042#include "core_jni_helpers.h"
Ruben Brunkb8df8e02014-06-02 22:59:45 -070043
Ruben Brunkf967a542014-04-28 16:31:11 -070044#include "android_runtime/AndroidRuntime.h"
45#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
46
47#include <jni.h>
48#include <JNIHelp.h>
49
50using namespace android;
51using namespace img_utils;
52
Ruben Brunk20796122015-07-21 17:51:54 -070053#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070054 if ((expr) != OK) { \
55 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070056 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070057 return false; \
Ruben Brunkf967a542014-04-28 16:31:11 -070058 }
59
Ruben Brunk20796122015-07-21 17:51:54 -070060
61#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
62 if ((expr) != OK) { \
63 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
64 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
65 return nullptr; \
66 }
67
68
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070069#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
70 if ((expr) != OK) { \
71 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
72 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
73 return -1; \
74 }
75
Ruben Brunk20796122015-07-21 17:51:54 -070076#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070077 if (entry.count == 0) { \
78 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070079 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070080 return nullptr; \
Ruben Brunkf967a542014-04-28 16:31:11 -070081 }
82
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -080083#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
84 if (expr) { \
85 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
86 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
87 return nullptr; \
88 }
89
Ruben Brunk20796122015-07-21 17:51:54 -070090
Ruben Brunkb6079002014-05-22 12:33:54 -070091#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070092
93static struct {
94 jfieldID mNativeContext;
95} gDngCreatorClassInfo;
96
97static struct {
98 jmethodID mWriteMethod;
99} gOutputStreamClassInfo;
100
Ruben Brunk47e91f22014-05-28 18:38:42 -0700101static struct {
102 jmethodID mReadMethod;
103 jmethodID mSkipMethod;
104} gInputStreamClassInfo;
105
106static struct {
107 jmethodID mGetMethod;
108} gInputByteBufferClassInfo;
109
Ruben Brunkf967a542014-04-28 16:31:11 -0700110enum {
111 BITS_PER_SAMPLE = 16,
112 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700113 BYTES_PER_RGB_PIXEL = 3,
114 BITS_PER_RGB_SAMPLE = 8,
115 BYTES_PER_RGB_SAMPLE = 1,
116 SAMPLES_PER_RGB_PIXEL = 3,
117 SAMPLES_PER_RAW_PIXEL = 1,
118 TIFF_IFD_0 = 0,
119 TIFF_IFD_SUB1 = 1,
120 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700121};
122
Ruben Brunk20796122015-07-21 17:51:54 -0700123
124/**
125 * POD container class for GPS tag data.
126 */
127class GpsData {
128public:
129 enum {
130 GPS_VALUE_LENGTH = 6,
131 GPS_REF_LENGTH = 2,
132 GPS_DATE_LENGTH = 11,
133 };
134
135 uint32_t mLatitude[GPS_VALUE_LENGTH];
136 uint32_t mLongitude[GPS_VALUE_LENGTH];
137 uint32_t mTimestamp[GPS_VALUE_LENGTH];
138 uint8_t mLatitudeRef[GPS_REF_LENGTH];
139 uint8_t mLongitudeRef[GPS_REF_LENGTH];
140 uint8_t mDate[GPS_DATE_LENGTH];
141};
142
Ruben Brunkf967a542014-04-28 16:31:11 -0700143// ----------------------------------------------------------------------------
144
Ruben Brunk47e91f22014-05-28 18:38:42 -0700145/**
146 * Container class for the persistent native context.
147 */
148
149class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700150public:
Ruben Brunk20796122015-07-21 17:51:54 -0700151 enum {
152 DATETIME_COUNT = 20,
153 };
154
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700155 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700156 virtual ~NativeContext();
157
158 TiffWriter* getWriter();
159
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700160 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
161 std::shared_ptr<const CameraMetadata> getResult() const;
162
Ruben Brunk20796122015-07-21 17:51:54 -0700163 uint32_t getThumbnailWidth() const;
164 uint32_t getThumbnailHeight() const;
165 const uint8_t* getThumbnail() const;
166 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700167
168 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
169
Ruben Brunk20796122015-07-21 17:51:54 -0700170 void setOrientation(uint16_t orientation);
171 uint16_t getOrientation() const;
172
173 void setDescription(const String8& desc);
174 String8 getDescription() const;
175 bool hasDescription() const;
176
177 void setGpsData(const GpsData& data);
178 GpsData getGpsData() const;
179 bool hasGpsData() const;
180
181 void setCaptureTime(const String8& formattedCaptureTime);
182 String8 getCaptureTime() const;
183 bool hasCaptureTime() const;
184
Ruben Brunk47e91f22014-05-28 18:38:42 -0700185private:
186 Vector<uint8_t> mCurrentThumbnail;
187 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700188 std::shared_ptr<CameraMetadata> mCharacteristics;
189 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700190 uint32_t mThumbnailWidth;
191 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700192 uint16_t mOrientation;
193 bool mThumbnailSet;
194 bool mGpsSet;
195 bool mDescriptionSet;
196 bool mCaptureTimeSet;
197 String8 mDescription;
198 GpsData mGpsData;
199 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700200};
201
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700202NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
203 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
204 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -0800205 mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
206 mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700207
208NativeContext::~NativeContext() {}
209
210TiffWriter* NativeContext::getWriter() {
211 return &mWriter;
212}
213
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700214std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
215 return mCharacteristics;
216}
217
218std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
219 return mResult;
220}
221
Ruben Brunk20796122015-07-21 17:51:54 -0700222uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700223 return mThumbnailWidth;
224}
225
Ruben Brunk20796122015-07-21 17:51:54 -0700226uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700227 return mThumbnailHeight;
228}
229
Ruben Brunk20796122015-07-21 17:51:54 -0700230const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700231 return mCurrentThumbnail.array();
232}
233
Ruben Brunk20796122015-07-21 17:51:54 -0700234bool NativeContext::hasThumbnail() const {
235 return mThumbnailSet;
236}
237
Ruben Brunk47e91f22014-05-28 18:38:42 -0700238bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
239 mThumbnailWidth = width;
240 mThumbnailHeight = height;
241
242 size_t size = BYTES_PER_RGB_PIXEL * width * height;
243 if (mCurrentThumbnail.resize(size) < 0) {
244 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
245 return false;
246 }
247
248 uint8_t* thumb = mCurrentThumbnail.editArray();
249 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700250 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700251 return true;
252}
253
Ruben Brunk20796122015-07-21 17:51:54 -0700254void NativeContext::setOrientation(uint16_t orientation) {
255 mOrientation = orientation;
256}
257
258uint16_t NativeContext::getOrientation() const {
259 return mOrientation;
260}
261
262void NativeContext::setDescription(const String8& desc) {
263 mDescription = desc;
264 mDescriptionSet = true;
265}
266
267String8 NativeContext::getDescription() const {
268 return mDescription;
269}
270
271bool NativeContext::hasDescription() const {
272 return mDescriptionSet;
273}
274
275void NativeContext::setGpsData(const GpsData& data) {
276 mGpsData = data;
277 mGpsSet = true;
278}
279
280GpsData NativeContext::getGpsData() const {
281 return mGpsData;
282}
283
284bool NativeContext::hasGpsData() const {
285 return mGpsSet;
286}
287
288void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
289 mFormattedCaptureTime = formattedCaptureTime;
290 mCaptureTimeSet = true;
291}
292
293String8 NativeContext::getCaptureTime() const {
294 return mFormattedCaptureTime;
295}
296
297bool NativeContext::hasCaptureTime() const {
298 return mCaptureTimeSet;
299}
300
Ruben Brunk47e91f22014-05-28 18:38:42 -0700301// End of NativeContext
302// ----------------------------------------------------------------------------
303
304/**
305 * Wrapper class for a Java OutputStream.
306 *
307 * This class is not intended to be used across JNI calls.
308 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700309class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
310public:
311 JniOutputStream(JNIEnv* env, jobject outStream);
312
313 virtual ~JniOutputStream();
314
315 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700316
Ruben Brunkf967a542014-04-28 16:31:11 -0700317 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700318
Ruben Brunkf967a542014-04-28 16:31:11 -0700319 status_t close();
320private:
321 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700322 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700323 };
324 jobject mOutputStream;
325 JNIEnv* mEnv;
326 jbyteArray mByteArray;
327};
328
329JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
330 mEnv(env) {
331 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700332 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700333 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
334 }
335}
336
337JniOutputStream::~JniOutputStream() {
338 mEnv->DeleteLocalRef(mByteArray);
339}
340
341status_t JniOutputStream::open() {
342 // Do nothing
343 return OK;
344}
345
346status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
347 while(count > 0) {
348 size_t len = BYTE_ARRAY_LENGTH;
349 len = (count > len) ? len : count;
350 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
351
352 if (mEnv->ExceptionCheck()) {
353 return BAD_VALUE;
354 }
355
356 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
357 0, len);
358
359 if (mEnv->ExceptionCheck()) {
360 return BAD_VALUE;
361 }
362
363 count -= len;
364 offset += len;
365 }
366 return OK;
367}
368
369status_t JniOutputStream::close() {
370 // Do nothing
371 return OK;
372}
373
Ruben Brunk47e91f22014-05-28 18:38:42 -0700374// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700375// ----------------------------------------------------------------------------
376
Ruben Brunk47e91f22014-05-28 18:38:42 -0700377/**
378 * Wrapper class for a Java InputStream.
379 *
380 * This class is not intended to be used across JNI calls.
381 */
382class JniInputStream : public Input, public LightRefBase<JniInputStream> {
383public:
384 JniInputStream(JNIEnv* env, jobject inStream);
385
386 status_t open();
387
388 status_t close();
389
390 ssize_t read(uint8_t* buf, size_t offset, size_t count);
391
392 ssize_t skip(size_t count);
393
394 virtual ~JniInputStream();
395private:
396 enum {
397 BYTE_ARRAY_LENGTH = 4096
398 };
399 jobject mInStream;
400 JNIEnv* mEnv;
401 jbyteArray mByteArray;
402
403};
404
405JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
406 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700407 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700408 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
409 }
410}
411
412JniInputStream::~JniInputStream() {
413 mEnv->DeleteLocalRef(mByteArray);
414}
415
416ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
417
418 jint realCount = BYTE_ARRAY_LENGTH;
419 if (count < BYTE_ARRAY_LENGTH) {
420 realCount = count;
421 }
422 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
423 realCount);
424
425 if (actual < 0) {
426 return NOT_ENOUGH_DATA;
427 }
428
429 if (mEnv->ExceptionCheck()) {
430 return BAD_VALUE;
431 }
432
433 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
434 if (mEnv->ExceptionCheck()) {
435 return BAD_VALUE;
436 }
437 return actual;
438}
439
440ssize_t JniInputStream::skip(size_t count) {
441 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
442 static_cast<jlong>(count));
443
444 if (mEnv->ExceptionCheck()) {
445 return BAD_VALUE;
446 }
447 if (actual < 0) {
448 return NOT_ENOUGH_DATA;
449 }
450 return actual;
451}
452
453status_t JniInputStream::open() {
454 // Do nothing
455 return OK;
456}
457
458status_t JniInputStream::close() {
459 // Do nothing
460 return OK;
461}
462
463// End of JniInputStream
464// ----------------------------------------------------------------------------
465
466/**
467 * Wrapper class for a non-direct Java ByteBuffer.
468 *
469 * This class is not intended to be used across JNI calls.
470 */
471class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
472public:
473 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
474
475 status_t open();
476
477 status_t close();
478
479 ssize_t read(uint8_t* buf, size_t offset, size_t count);
480
481 virtual ~JniInputByteBuffer();
482private:
483 enum {
484 BYTE_ARRAY_LENGTH = 4096
485 };
486 jobject mInBuf;
487 JNIEnv* mEnv;
488 jbyteArray mByteArray;
489};
490
491JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
492 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700493 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700494 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
495 }
496}
497
498JniInputByteBuffer::~JniInputByteBuffer() {
499 mEnv->DeleteLocalRef(mByteArray);
500}
501
502ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
503 jint realCount = BYTE_ARRAY_LENGTH;
504 if (count < BYTE_ARRAY_LENGTH) {
505 realCount = count;
506 }
507
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700508 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
509 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800510 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700511
512 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800513 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700514 return BAD_VALUE;
515 }
516
517 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
518 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800519 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700520 return BAD_VALUE;
521 }
522 return realCount;
523}
524
525status_t JniInputByteBuffer::open() {
526 // Do nothing
527 return OK;
528}
529
530status_t JniInputByteBuffer::close() {
531 // Do nothing
532 return OK;
533}
534
535// End of JniInputByteBuffer
536// ----------------------------------------------------------------------------
537
538/**
539 * StripSource subclass for Input types.
540 *
541 * This class is not intended to be used across JNI calls.
542 */
543
544class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
545public:
546 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
547 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
548 uint32_t samplesPerPixel);
549
550 virtual ~InputStripSource();
551
552 virtual status_t writeToStream(Output& stream, uint32_t count);
553
554 virtual uint32_t getIfd() const;
555protected:
556 uint32_t mIfd;
557 Input* mInput;
558 uint32_t mWidth;
559 uint32_t mHeight;
560 uint32_t mPixStride;
561 uint32_t mRowStride;
562 uint64_t mOffset;
563 JNIEnv* mEnv;
564 uint32_t mBytesPerSample;
565 uint32_t mSamplesPerPixel;
566};
567
568InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
569 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
570 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
571 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
572 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
573 mSamplesPerPixel(samplesPerPixel) {}
574
575InputStripSource::~InputStripSource() {}
576
577status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700578 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700579 jlong offset = mOffset;
580
581 if (fullSize != count) {
582 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
583 fullSize);
584 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
585 return BAD_VALUE;
586 }
587
588 // Skip offset
589 while (offset > 0) {
590 ssize_t skipped = mInput->skip(offset);
591 if (skipped <= 0) {
592 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
593 jniThrowExceptionFmt(mEnv, "java/io/IOException",
594 "Early EOF encountered in skip, not enough pixel data for image of size %u",
595 fullSize);
596 skipped = NOT_ENOUGH_DATA;
597 } else {
598 if (!mEnv->ExceptionCheck()) {
599 jniThrowException(mEnv, "java/io/IOException",
600 "Error encountered while skip bytes in input stream.");
601 }
602 }
603
604 return skipped;
605 }
606 offset -= skipped;
607 }
608
609 Vector<uint8_t> row;
610 if (row.resize(mRowStride) < 0) {
611 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
612 return BAD_VALUE;
613 }
614
615 uint8_t* rowBytes = row.editArray();
616
617 for (uint32_t i = 0; i < mHeight; ++i) {
618 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800619 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700620
621 while (rowFillAmt < mRowStride) {
622 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
623 if (bytesRead <= 0) {
624 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800625 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
626 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700627 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800628 "Early EOF encountered, not enough pixel data for image of size %"
629 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700630 bytesRead = NOT_ENOUGH_DATA;
631 } else {
632 if (!mEnv->ExceptionCheck()) {
633 jniThrowException(mEnv, "java/io/IOException",
634 "Error encountered while reading");
635 }
636 }
637 return bytesRead;
638 }
639 rowFillAmt += bytesRead;
640 rowSize -= bytesRead;
641 }
642
643 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
644 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
645
646 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
647 mEnv->ExceptionCheck()) {
648 if (!mEnv->ExceptionCheck()) {
649 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
650 }
651 return BAD_VALUE;
652 }
653 } else {
654 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
655 jniThrowException(mEnv, "java/lang/IllegalStateException",
656 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
657 return BAD_VALUE;
658
659 // TODO: Add support for non-contiguous pixels if needed.
660 }
661 }
662 return OK;
663}
664
665uint32_t InputStripSource::getIfd() const {
666 return mIfd;
667}
668
669// End of InputStripSource
670// ----------------------------------------------------------------------------
671
672/**
673 * StripSource subclass for direct buffer types.
674 *
675 * This class is not intended to be used across JNI calls.
676 */
677
678class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
679public:
680 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
681 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
682 uint32_t bytesPerSample, uint32_t samplesPerPixel);
683
684 virtual ~DirectStripSource();
685
686 virtual status_t writeToStream(Output& stream, uint32_t count);
687
688 virtual uint32_t getIfd() const;
689protected:
690 uint32_t mIfd;
691 const uint8_t* mPixelBytes;
692 uint32_t mWidth;
693 uint32_t mHeight;
694 uint32_t mPixStride;
695 uint32_t mRowStride;
696 uint16_t mOffset;
697 JNIEnv* mEnv;
698 uint32_t mBytesPerSample;
699 uint32_t mSamplesPerPixel;
700};
701
702DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
703 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
704 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
705 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
706 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
707 mSamplesPerPixel(samplesPerPixel) {}
708
709DirectStripSource::~DirectStripSource() {}
710
711status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700712 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700713
714 if (fullSize != count) {
715 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
716 fullSize);
717 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
718 return BAD_VALUE;
719 }
720
Ruben Brunk20796122015-07-21 17:51:54 -0700721
Ruben Brunk47e91f22014-05-28 18:38:42 -0700722 if (mPixStride == mBytesPerSample * mSamplesPerPixel
723 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
724 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
725
726 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
727 if (!mEnv->ExceptionCheck()) {
728 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
729 }
730 return BAD_VALUE;
731 }
732 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
733 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
734
735 for (size_t i = 0; i < mHeight; ++i) {
736 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
737 mEnv->ExceptionCheck()) {
738 if (!mEnv->ExceptionCheck()) {
739 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
740 }
741 return BAD_VALUE;
742 }
743 }
744 } else {
745 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
746
747 jniThrowException(mEnv, "java/lang/IllegalStateException",
748 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
749 return BAD_VALUE;
750
751 // TODO: Add support for non-contiguous pixels if needed.
752 }
753 return OK;
754
755}
756
757uint32_t DirectStripSource::getIfd() const {
758 return mIfd;
759}
760
761// End of DirectStripSource
762// ----------------------------------------------------------------------------
763
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700764/**
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700765 * Calculate the default crop relative to the "active area" of the image sensor (this active area
766 * will always be the pre-correction active area rectangle), and set this.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700767 */
768static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700769 sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700770
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700771 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700772 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700773 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
774 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
775
Ruben Brunk20796122015-07-21 17:51:54 -0700776 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
777
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700778 if (width < margin * 2 || height < margin * 2) {
779 ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too"
780 "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width);
781 jniThrowException(env, "java/lang/IllegalStateException",
782 "Pre-correction active area is too small.");
783 return BAD_VALUE;
Ruben Brunk20796122015-07-21 17:51:54 -0700784 }
785
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700786 uint32_t defaultCropOrigin[] = {margin, margin};
787 uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin,
788 height - defaultCropOrigin[1] - margin};
789
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700790 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
791 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
792 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
793 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
794
795 return OK;
796}
797
Ruben Brunk20796122015-07-21 17:51:54 -0700798static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700799 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700800 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700801 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700802 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700803 return false;
804 }
805
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700806 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700807 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700808 "Image height %d is invalid", height);
809 return false;
810 }
811
812 camera_metadata_ro_entry preCorrectionEntry =
813 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
814 camera_metadata_ro_entry pixelArrayEntry =
815 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
816
817 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
818 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
819 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
820 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
821
822 bool matchesPixelArray = (pWidth == width && pHeight == height);
823 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
824
Ruben Brunk20796122015-07-21 17:51:54 -0700825 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700826 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
827 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
828 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
829 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700830 return false;
831 }
832
833 return true;
834}
835
Ruben Brunk20796122015-07-21 17:51:54 -0700836static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700837 const Vector<uint16_t>& entries) {
838 for (size_t i = 0; i < entries.size(); ++i) {
839 uint16_t tagId = entries[i];
840 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700841 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700842 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
843 ifdFrom);
844 return BAD_VALUE;
845 }
846 if (writer->addEntry(entry, ifdTo) != OK) {
847 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
848 ifdFrom);
849 return BAD_VALUE;
850 }
851 writer->removeEntry(tagId, ifdFrom);
852 }
853 return OK;
854}
855
Ruben Brunkd70132c2014-08-22 16:24:49 -0700856/**
857 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
858 * Returns OK on success, or a negative error code if the CFA enum was invalid.
859 */
860static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
861 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
862 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
863 cfaEnum);
864 switch(cfa) {
865 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
866 cfaOut[0] = 0;
867 cfaOut[1] = 1;
868 cfaOut[2] = 1;
869 cfaOut[3] = 2;
870 break;
871 }
872 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
873 cfaOut[0] = 1;
874 cfaOut[1] = 0;
875 cfaOut[2] = 2;
876 cfaOut[3] = 1;
877 break;
878 }
879 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
880 cfaOut[0] = 1;
881 cfaOut[1] = 2;
882 cfaOut[2] = 0;
883 cfaOut[3] = 1;
884 break;
885 }
886 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
887 cfaOut[0] = 2;
888 cfaOut[1] = 1;
889 cfaOut[2] = 1;
890 cfaOut[3] = 0;
891 break;
892 }
893 default: {
894 return BAD_VALUE;
895 }
896 }
897 return OK;
898}
899
900/**
901 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
902 * RGGB for an unknown enum.
903 */
904static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
905 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
906 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
907 cfaEnum);
908 switch(cfa) {
909 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
910 return OpcodeListBuilder::CFA_RGGB;
911 }
912 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
913 return OpcodeListBuilder::CFA_GRBG;
914 }
915 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
916 return OpcodeListBuilder::CFA_GBRG;
917 }
918 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
919 return OpcodeListBuilder::CFA_BGGR;
920 }
921 default: {
922 return OpcodeListBuilder::CFA_RGGB;
923 }
924 }
925}
926
927/**
928 * For each color plane, find the corresponding noise profile coefficients given in the
929 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
930 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
931 *
932 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
933 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
934 * coefficients.
935 * numChannels - the number of noise profile coefficient pairs and color channels given in
936 * the perChannelNoiseProfile and cfa arguments, respectively.
937 * planeColors - the color planes in the noise profile output.
938 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
939 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
940 *
941 * returns OK, or a negative error code on failure.
942 */
943static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
944 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
945 /*out*/double* noiseProfile) {
946
947 for (size_t p = 0; p < numPlanes; ++p) {
948 size_t S = p * 2;
949 size_t O = p * 2 + 1;
950
951 noiseProfile[S] = 0;
952 noiseProfile[O] = 0;
953 bool uninitialized = true;
954 for (size_t c = 0; c < numChannels; ++c) {
955 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
956 noiseProfile[S] = perChannelNoiseProfile[c * 2];
957 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
958 uninitialized = false;
959 }
960 }
961 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800962 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
963 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700964 return BAD_VALUE;
965 }
966 }
967 return OK;
968}
969
Ruben Brunk47e91f22014-05-28 18:38:42 -0700970// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700971extern "C" {
972
Ruben Brunk47e91f22014-05-28 18:38:42 -0700973static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700974 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700975 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700976 gDngCreatorClassInfo.mNativeContext));
977}
978
Ruben Brunk47e91f22014-05-28 18:38:42 -0700979static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700980 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700981 NativeContext* current = DngCreator_getNativeContext(env, thiz);
982
Ruben Brunk20796122015-07-21 17:51:54 -0700983 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700984 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700985 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700986
Ruben Brunkf967a542014-04-28 16:31:11 -0700987 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700988 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700989 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700990
Ruben Brunkf967a542014-04-28 16:31:11 -0700991 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700992 reinterpret_cast<jlong>(context.get()));
993}
994
Ruben Brunkf967a542014-04-28 16:31:11 -0700995static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
996 ALOGV("%s:", __FUNCTION__);
997
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800998 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
999 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001000
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001001 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1002 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1003 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001004
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001005 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1006 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1007 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001008
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001009 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1010 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1011 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001012}
1013
1014static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001015 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001016 ALOGV("%s:", __FUNCTION__);
1017 CameraMetadata characteristics;
1018 CameraMetadata results;
1019 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1020 jniThrowException(env, "java/lang/AssertionError",
1021 "No native metadata defined for camera characteristics.");
1022 return;
1023 }
1024 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1025 jniThrowException(env, "java/lang/AssertionError",
1026 "No native metadata defined for capture results.");
1027 return;
1028 }
1029
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001030 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001031
1032 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1033
1034 size_t len = strlen(captureTime) + 1;
1035 if (len != NativeContext::DATETIME_COUNT) {
1036 jniThrowException(env, "java/lang/IllegalArgumentException",
1037 "Formatted capture time string length is not required 20 characters");
1038 return;
1039 }
1040
1041 nativeContext->setCaptureTime(String8(captureTime));
1042
1043 DngCreator_setNativeContext(env, thiz, nativeContext);
1044}
1045
1046static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1047 uint32_t imageHeight) {
1048
1049 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1050
1051 if (nativeContext == nullptr) {
1052 jniThrowException(env, "java/lang/AssertionError",
1053 "No native context, must call init before other operations.");
1054 return nullptr;
1055 }
1056
1057 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1058 CameraMetadata results = *(nativeContext->getResult());
1059
1060 sp<TiffWriter> writer = new TiffWriter();
1061
1062 uint32_t preWidth = 0;
1063 uint32_t preHeight = 0;
1064 {
1065 // Check dimensions
1066 camera_metadata_entry entry =
1067 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1068 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1069 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1070 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1071
1072 camera_metadata_entry pixelArrayEntry =
1073 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1074 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1075 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1076
1077 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1078 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1079 jniThrowException(env, "java/lang/AssertionError",
1080 "Height and width of imate buffer did not match height and width of"
1081 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1082 return nullptr;
1083 }
1084 }
1085
1086
Ruben Brunkf967a542014-04-28 16:31:11 -07001087
1088 writer->addIfd(TIFF_IFD_0);
1089
1090 status_t err = OK;
1091
1092 const uint32_t samplesPerPixel = 1;
1093 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001094
1095 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001096 uint8_t cfaPlaneColor[3] = {0, 1, 2};
1097 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001098
1099 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001100 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001101
1102 // Setup main image tags
1103
Ruben Brunkf967a542014-04-28 16:31:11 -07001104 {
1105 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001106 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001107 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1108 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001109 }
1110
1111 {
1112 // Set subfiletype
1113 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001114 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1115 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001116 }
1117
1118 {
1119 // Set bits per sample
1120 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001121 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001122 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001123 }
1124
1125 {
1126 // Set compression
1127 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001128 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1129 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001130 }
1131
1132 {
1133 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001134 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1135 env, TAG_IMAGEWIDTH, writer);
1136 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1137 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001138 }
1139
1140 {
1141 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -07001142 uint16_t interpretation = 32803; // CFA
Ruben Brunk20796122015-07-21 17:51:54 -07001143 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1144 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001145 }
1146
1147 {
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001148 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001149 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001150 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1151 uint32_t blackLevelRational[8] = {0};
1152 if (entry.count != 0) {
1153 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1154 for (size_t i = 0; i < entry.count; i++) {
1155 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1156 blackLevelRational[i * 2 + 1] = 100;
1157 }
1158 } else {
1159 // Fall back to static black level which is guaranteed
1160 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1161 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1162 for (size_t i = 0; i < entry.count; i++) {
1163 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1164 blackLevelRational[i * 2 + 1] = 1;
1165 }
1166
1167 }
1168 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
Ruben Brunk20796122015-07-21 17:51:54 -07001169 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001170
1171 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001172 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1173 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001174 }
1175
1176 {
1177 // Set samples per pixel
1178 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001179 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001180 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001181 }
1182
1183 {
1184 // Set planar configuration
1185 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001186 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1187 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001188 }
1189
1190 {
1191 // Set CFA pattern dimensions
1192 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001193 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1194 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001195 }
1196
1197 {
1198 // Set CFA pattern
1199 camera_metadata_entry entry =
1200 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk20796122015-07-21 17:51:54 -07001201 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001202
1203 const int cfaLength = 4;
1204 cfaEnum = entry.data.u8[0];
1205 uint8_t cfa[cfaLength];
1206 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1207 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1208 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001209 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001210
Ruben Brunk20796122015-07-21 17:51:54 -07001211 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1212 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001213
1214 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001215 }
1216
1217 {
1218 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001219 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1220 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001221 }
1222
1223 {
1224 // Set CFA layout
1225 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001226 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001227 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001228 }
1229
1230 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001231 // image description
1232 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001233 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1234 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001235 }
1236
1237 {
1238 // make
1239 char manufacturer[PROPERTY_VALUE_MAX];
1240
1241 // Use "" to represent unknown make as suggested in TIFF/EP spec.
1242 property_get("ro.product.manufacturer", manufacturer, "");
1243 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1244
Ruben Brunk20796122015-07-21 17:51:54 -07001245 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
1246 reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001247 }
1248
1249 {
1250 // model
1251 char model[PROPERTY_VALUE_MAX];
1252
1253 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1254 property_get("ro.product.model", model, "");
1255 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1256
Ruben Brunk20796122015-07-21 17:51:54 -07001257 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
1258 reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001259 }
1260
1261 {
1262 // x resolution
1263 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001264 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001265 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001266
1267 // y resolution
1268 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001269 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001270 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001271
1272 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001273 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001274 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001275 }
1276
1277 {
1278 // software
1279 char software[PROPERTY_VALUE_MAX];
1280 property_get("ro.build.fingerprint", software, "");
1281 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001282 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
1283 reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001284 }
1285
Ruben Brunk20796122015-07-21 17:51:54 -07001286 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001287 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001288 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001289
Ruben Brunk20796122015-07-21 17:51:54 -07001290 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1291 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001292 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1293 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001294 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001295 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001296
1297 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001298 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1299 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001300 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1301 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001302 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001303 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001304 }
1305
1306 {
1307 // TIFF/EP standard id
1308 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001309 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001310 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001311 }
1312
1313 {
1314 // copyright
1315 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001316 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001317 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001318 }
1319
1320 {
1321 // exposure time
1322 camera_metadata_entry entry =
1323 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001324 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001325
1326 int64_t exposureTime = *(entry.data.i64);
1327
1328 if (exposureTime < 0) {
1329 // Should be unreachable
1330 jniThrowException(env, "java/lang/IllegalArgumentException",
1331 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001332 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001333 }
1334
1335 // Ensure exposure time doesn't overflow (for exposures > 4s)
1336 uint32_t denominator = 1000000000;
1337 while (exposureTime > UINT32_MAX) {
1338 exposureTime >>= 1;
1339 denominator >>= 1;
1340 if (denominator == 0) {
1341 // Should be unreachable
1342 jniThrowException(env, "java/lang/IllegalArgumentException",
1343 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001344 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001345 }
1346 }
1347
1348 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001349 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001350 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001351
1352 }
1353
1354 {
1355 // ISO speed ratings
1356 camera_metadata_entry entry =
1357 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001358 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001359
1360 int32_t tempIso = *(entry.data.i32);
1361 if (tempIso < 0) {
1362 jniThrowException(env, "java/lang/IllegalArgumentException",
1363 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001364 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001365 }
1366
1367 if (tempIso > UINT16_MAX) {
1368 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1369 tempIso = UINT16_MAX;
1370 }
1371
1372 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001373 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001374 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001375 }
1376
1377 {
1378 // focal length
1379 camera_metadata_entry entry =
1380 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001381 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001382
1383 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001384 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001385 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001386 }
1387
1388 {
1389 // f number
1390 camera_metadata_entry entry =
1391 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001392 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001393
1394 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001395 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001396 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001397 }
1398
1399 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001400 // Set DNG version information
1401 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001402 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001403 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001404
1405 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001406 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1407 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001408 }
1409
1410 {
1411 // Set whitelevel
1412 camera_metadata_entry entry =
1413 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001414 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001415 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001416 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1417 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001418 }
1419
1420 {
1421 // Set default scale
1422 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001423 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1424 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001425 }
1426
1427 bool singleIlluminant = false;
1428 {
1429 // Set calibration illuminants
1430 camera_metadata_entry entry1 =
1431 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001432 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001433 camera_metadata_entry entry2 =
1434 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1435 if (entry2.count == 0) {
1436 singleIlluminant = true;
1437 }
1438 uint16_t ref1 = entry1.data.u8[0];
1439
Ruben Brunk20796122015-07-21 17:51:54 -07001440 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001441 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001442
1443 if (!singleIlluminant) {
1444 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001445 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001446 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001447 }
1448 }
1449
1450 {
1451 // Set color transforms
1452 camera_metadata_entry entry1 =
1453 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001454 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001455
1456 int32_t colorTransform1[entry1.count * 2];
1457
1458 size_t ctr = 0;
1459 for(size_t i = 0; i < entry1.count; ++i) {
1460 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1461 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1462 }
1463
Ruben Brunk20796122015-07-21 17:51:54 -07001464 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1465 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001466
1467 if (!singleIlluminant) {
1468 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001469 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001470 int32_t colorTransform2[entry2.count * 2];
1471
1472 ctr = 0;
1473 for(size_t i = 0; i < entry2.count; ++i) {
1474 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1475 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1476 }
1477
Ruben Brunk20796122015-07-21 17:51:54 -07001478 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1479 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001480 }
1481 }
1482
1483 {
1484 // Set calibration transforms
1485 camera_metadata_entry entry1 =
1486 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001487 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001488
1489 int32_t calibrationTransform1[entry1.count * 2];
1490
1491 size_t ctr = 0;
1492 for(size_t i = 0; i < entry1.count; ++i) {
1493 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1494 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1495 }
1496
Ruben Brunk20796122015-07-21 17:51:54 -07001497 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001498 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001499
1500 if (!singleIlluminant) {
1501 camera_metadata_entry entry2 =
1502 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001503 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001504 int32_t calibrationTransform2[entry2.count * 2];
1505
1506 ctr = 0;
1507 for(size_t i = 0; i < entry2.count; ++i) {
1508 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1509 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1510 }
1511
Ruben Brunk20796122015-07-21 17:51:54 -07001512 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001513 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001514 }
1515 }
1516
1517 {
1518 // Set forward transforms
1519 camera_metadata_entry entry1 =
1520 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001521 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001522
1523 int32_t forwardTransform1[entry1.count * 2];
1524
1525 size_t ctr = 0;
1526 for(size_t i = 0; i < entry1.count; ++i) {
1527 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1528 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1529 }
1530
Ruben Brunk20796122015-07-21 17:51:54 -07001531 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1532 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001533
1534 if (!singleIlluminant) {
1535 camera_metadata_entry entry2 =
1536 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001537 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001538 int32_t forwardTransform2[entry2.count * 2];
1539
1540 ctr = 0;
1541 for(size_t i = 0; i < entry2.count; ++i) {
1542 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1543 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1544 }
1545
Ruben Brunk20796122015-07-21 17:51:54 -07001546 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1547 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001548 }
1549 }
1550
1551 {
1552 // Set camera neutral
1553 camera_metadata_entry entry =
1554 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001555 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001556 uint32_t cameraNeutral[entry.count * 2];
1557
1558 size_t ctr = 0;
1559 for(size_t i = 0; i < entry.count; ++i) {
1560 cameraNeutral[ctr++] =
1561 static_cast<uint32_t>(entry.data.r[i].numerator);
1562 cameraNeutral[ctr++] =
1563 static_cast<uint32_t>(entry.data.r[i].denominator);
1564 }
1565
Ruben Brunk20796122015-07-21 17:51:54 -07001566 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001567 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001568 }
1569
Ruben Brunkf967a542014-04-28 16:31:11 -07001570
1571 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001572 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001573 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001574 return nullptr;
1575 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001576 camera_metadata_entry entry =
1577 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001578 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001579 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1580 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1581 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1582 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001583
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001584 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1585 // relative to the pixel array.
1586 if (imageWidth == width && imageHeight == height) {
1587 xmin = 0;
1588 ymin = 0;
1589 }
1590
Ruben Brunk20796122015-07-21 17:51:54 -07001591 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1592 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1593 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001594 }
1595
1596 {
1597 // Setup unique camera model tag
1598 char model[PROPERTY_VALUE_MAX];
1599 property_get("ro.product.model", model, "");
1600
1601 char manufacturer[PROPERTY_VALUE_MAX];
1602 property_get("ro.product.manufacturer", manufacturer, "");
1603
1604 char brand[PROPERTY_VALUE_MAX];
1605 property_get("ro.product.brand", brand, "");
1606
1607 String8 cameraModel(model);
1608 cameraModel += "-";
1609 cameraModel += manufacturer;
1610 cameraModel += "-";
1611 cameraModel += brand;
1612
Ruben Brunk20796122015-07-21 17:51:54 -07001613 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001614 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001615 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001616 }
1617
1618 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001619 // Setup sensor noise model
1620 camera_metadata_entry entry =
1621 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1622
Ruben Brunkd70132c2014-08-22 16:24:49 -07001623 const status_t numPlaneColors = 3;
1624 const status_t numCfaChannels = 4;
1625
1626 uint8_t cfaOut[numCfaChannels];
1627 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1628 jniThrowException(env, "java/lang/IllegalArgumentException",
1629 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001630 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001631 }
1632
1633 double noiseProfile[numPlaneColors * 2];
1634
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001635 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001636 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001637 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1638 "in characteristics, no noise profile tag written...",
1639 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001640 } else {
1641 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1642 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1643
Ruben Brunk20796122015-07-21 17:51:54 -07001644 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1645 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1646 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001647 } else {
1648 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1649 " tag written...", __FUNCTION__);
1650 }
1651 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001652 } else {
1653 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1654 __FUNCTION__);
1655 }
1656 }
1657
1658 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001659 // Set up opcode List 2
1660 OpcodeListBuilder builder;
1661 status_t err = OK;
1662
1663 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001664 camera_metadata_entry entry1 =
1665 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001666
1667 uint32_t lsmWidth = 0;
1668 uint32_t lsmHeight = 0;
1669
1670 if (entry1.count != 0) {
1671 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1672 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1673 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001674
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001675 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001676
Ruben Brunk20796122015-07-21 17:51:54 -07001677 camera_metadata_entry entry =
1678 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1679 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1680 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1681 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1682 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1683 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001684 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001685 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001686 err = builder.addGainMapsForMetadata(lsmWidth,
1687 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001688 0,
1689 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001690 height,
1691 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001692 opcodeCfaLayout,
1693 entry2.data.f);
1694 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001695 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1696 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001697 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001698 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001699 }
1700
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001701
1702 // Set up bad pixel correction list
1703 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
1704
1705 if ((entry3.count % 2) != 0) {
1706 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1707 __FUNCTION__);
1708 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1709 return nullptr;
1710 }
1711
1712 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1713 std::vector<uint32_t> v;
1714 for (size_t i = 0; i < entry3.count; i+=2) {
1715 int32_t x = entry3.data.i32[i];
1716 int32_t y = entry3.data.i32[i + 1];
1717 x -= static_cast<int32_t>(xmin);
1718 y -= static_cast<int32_t>(ymin);
1719 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1720 static_cast<uint32_t>(y) >= width) {
1721 continue;
1722 }
1723 v.push_back(x);
1724 v.push_back(y);
1725 }
1726 const uint32_t* badPixels = &v[0];
1727 uint32_t badPixelCount = v.size();
1728
1729 if (badPixelCount > 0) {
1730 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1731
1732 if (err != OK) {
1733 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1734 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1735 return nullptr;
1736 }
1737 }
1738
1739
Ruben Brunkfe816622015-06-16 23:03:09 -07001740 size_t listSize = builder.getSize();
1741 uint8_t opcodeListBuf[listSize];
1742 err = builder.buildOpList(opcodeListBuf);
1743 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001744 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001745 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1746 } else {
1747 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1748 "map.", __FUNCTION__);
1749 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1750 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001751 return nullptr;
Ruben Brunkfe816622015-06-16 23:03:09 -07001752 }
1753 }
1754
1755 {
1756 // Set up opcode List 3
1757 OpcodeListBuilder builder;
1758 status_t err = OK;
1759
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001760 // Set up rectilinear distortion correction
1761 camera_metadata_entry entry3 =
1762 results.find(ANDROID_LENS_RADIAL_DISTORTION);
1763 camera_metadata_entry entry4 =
1764 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1765
1766 if (entry3.count == 6 && entry4.count == 5) {
1767 float cx = entry4.data.f[/*c_x*/2];
1768 float cy = entry4.data.f[/*c_y*/3];
Ruben Brunk20796122015-07-21 17:51:54 -07001769 err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001770 cy);
1771 if (err != OK) {
1772 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1773 jniThrowRuntimeException(env, "failed to add distortion correction.");
Ruben Brunk20796122015-07-21 17:51:54 -07001774 return nullptr;
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001775 }
1776 }
1777
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001778 size_t listSize = builder.getSize();
1779 uint8_t opcodeListBuf[listSize];
1780 err = builder.buildOpList(opcodeListBuf);
1781 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001782 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001783 TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001784 } else {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001785 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1786 "map.", __FUNCTION__);
1787 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1788 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001789 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001790 }
1791 }
1792
Ruben Brunk20796122015-07-21 17:51:54 -07001793 {
1794 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08001795 // Note: There's only one orientation field for the whole file, in IFD0
1796 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07001797 uint16_t orientation = nativeContext->getOrientation();
1798 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1799 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001800
Ruben Brunk47e91f22014-05-28 18:38:42 -07001801 }
1802
Ruben Brunk20796122015-07-21 17:51:54 -07001803 if (nativeContext->hasDescription()){
1804 // Set Description
1805 String8 description = nativeContext->getDescription();
1806 size_t len = description.bytes() + 1;
1807 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1808 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1809 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1810 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001811 }
1812 }
1813
Ruben Brunk20796122015-07-21 17:51:54 -07001814 if (nativeContext->hasGpsData()) {
1815 // Set GPS tags
1816 GpsData gpsData = nativeContext->getGpsData();
1817 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1818 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1819 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1820 TIFF_IFD_0);
1821 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1822 return nullptr;
1823 }
1824 }
1825
1826 {
1827 uint8_t version[] = {2, 3, 0, 0};
1828 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1829 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1830 }
1831
1832 {
1833 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1834 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1835 TAG_GPSLATITUDEREF, writer);
1836 }
1837
1838 {
1839 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1840 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1841 TAG_GPSLONGITUDEREF, writer);
1842 }
1843
1844 {
1845 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1846 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1847 }
1848
1849 {
1850 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1851 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1852 }
1853
1854 {
1855 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1856 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1857 }
1858
1859 {
1860 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1861 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1862 TAG_GPSDATESTAMP, writer);
1863 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001864 }
1865
Ruben Brunk47e91f22014-05-28 18:38:42 -07001866
Ruben Brunk20796122015-07-21 17:51:54 -07001867 if (nativeContext->hasThumbnail()) {
1868 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1869 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1870 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1871 TIFF_IFD_0);
1872 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1873 return nullptr;
1874 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001875 }
1876
1877 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001878 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001879 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001880 tagsToMove.add(TAG_BITSPERSAMPLE);
1881 tagsToMove.add(TAG_COMPRESSION);
1882 tagsToMove.add(TAG_IMAGEWIDTH);
1883 tagsToMove.add(TAG_IMAGELENGTH);
1884 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1885 tagsToMove.add(TAG_BLACKLEVEL);
1886 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1887 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1888 tagsToMove.add(TAG_PLANARCONFIGURATION);
1889 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1890 tagsToMove.add(TAG_CFAPATTERN);
1891 tagsToMove.add(TAG_CFAPLANECOLOR);
1892 tagsToMove.add(TAG_CFALAYOUT);
1893 tagsToMove.add(TAG_XRESOLUTION);
1894 tagsToMove.add(TAG_YRESOLUTION);
1895 tagsToMove.add(TAG_RESOLUTIONUNIT);
1896 tagsToMove.add(TAG_WHITELEVEL);
1897 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001898 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1899 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1900 tagsToMove.add(TAG_OPCODELIST2);
Ruben Brunkfe816622015-06-16 23:03:09 -07001901 tagsToMove.add(TAG_OPCODELIST3);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001902
1903 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1904 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07001905 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001906 }
1907
Ruben Brunk20796122015-07-21 17:51:54 -07001908 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001909
Ruben Brunk20796122015-07-21 17:51:54 -07001910 {
1911 // Set photometric interpretation
1912 uint16_t interpretation = 2; // RGB
1913 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1914 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001915 }
Ruben Brunk20796122015-07-21 17:51:54 -07001916
1917 {
1918 // Set planar configuration
1919 uint16_t config = 1; // Chunky
1920 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1921 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
1922 }
1923
1924 {
1925 // Set samples per pixel
1926 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1927 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
1928 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
1929 }
1930
1931 {
1932 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001933 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
1934 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
1935 BAIL_IF_INVALID_RET_NULL_SP(
1936 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07001937 env, TAG_BITSPERSAMPLE, writer);
1938 }
1939
1940 {
1941 // Set subfiletype
1942 uint32_t subfileType = 1; // Thumbnail image
1943 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1944 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
1945 }
1946
1947 {
1948 // Set compression
1949 uint16_t compression = 1; // None
1950 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1951 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
1952 }
1953
1954 {
1955 // Set dimensions
1956 uint32_t uWidth = nativeContext->getThumbnailWidth();
1957 uint32_t uHeight = nativeContext->getThumbnailHeight();
1958 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
1959 env, TAG_IMAGEWIDTH, writer);
1960 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
1961 env, TAG_IMAGELENGTH, writer);
1962 }
1963
1964 {
1965 // x resolution
1966 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1967 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1968 env, TAG_XRESOLUTION, writer);
1969
1970 // y resolution
1971 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1972 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1973 env, TAG_YRESOLUTION, writer);
1974
1975 uint16_t unit = 2; // inches
1976 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1977 env, TAG_RESOLUTIONUNIT, writer);
1978 }
1979 }
1980
1981 if (writer->addStrip(TIFF_IFD_0) != OK) {
1982 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1983 jniThrowException(env, "java/lang/IllegalStateException",
1984 "Failed to setup thumbnail strip tags.");
1985 return nullptr;
1986 }
1987
1988 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001989 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1990 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1991 jniThrowException(env, "java/lang/IllegalStateException",
1992 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07001993 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001994 }
1995 }
Ruben Brunk20796122015-07-21 17:51:54 -07001996 return writer;
1997}
1998
1999static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2000 ALOGV("%s:", __FUNCTION__);
2001 DngCreator_setNativeContext(env, thiz, nullptr);
2002}
2003
2004static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2005 ALOGV("%s:", __FUNCTION__);
2006
2007 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2008 if (context == nullptr) {
2009 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2010 jniThrowException(env, "java/lang/AssertionError",
2011 "setOrientation called with uninitialized DngCreator");
2012 return;
2013 }
2014
2015 uint16_t orientation = static_cast<uint16_t>(orient);
2016 context->setOrientation(orientation);
2017}
2018
2019static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2020 ALOGV("%s:", __FUNCTION__);
2021
2022 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2023 if (context == nullptr) {
2024 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2025 jniThrowException(env, "java/lang/AssertionError",
2026 "setDescription called with uninitialized DngCreator");
2027 return;
2028 }
2029
2030 const char* desc = env->GetStringUTFChars(description, nullptr);
2031 context->setDescription(String8(desc));
2032 env->ReleaseStringUTFChars(description, desc);
2033}
2034
2035static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2036 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2037 ALOGV("%s:", __FUNCTION__);
2038
2039 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2040 if (context == nullptr) {
2041 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2042 jniThrowException(env, "java/lang/AssertionError",
2043 "setGpsTags called with uninitialized DngCreator");
2044 return;
2045 }
2046
2047 GpsData data;
2048
2049 jsize latLen = env->GetArrayLength(latTag);
2050 jsize longLen = env->GetArrayLength(longTag);
2051 jsize timeLen = env->GetArrayLength(timeTag);
2052 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2053 jniThrowException(env, "java/lang/IllegalArgumentException",
2054 "invalid latitude tag length");
2055 return;
2056 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2057 jniThrowException(env, "java/lang/IllegalArgumentException",
2058 "invalid longitude tag length");
2059 return;
2060 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2061 jniThrowException(env, "java/lang/IllegalArgumentException",
2062 "invalid time tag length");
2063 return;
2064 }
2065
2066 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2067 reinterpret_cast<jint*>(&data.mLatitude));
2068 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2069 reinterpret_cast<jint*>(&data.mLongitude));
2070 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2071 reinterpret_cast<jint*>(&data.mTimestamp));
2072
2073
2074 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2075 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2076 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2077 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2078 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2079 reinterpret_cast<char*>(&data.mDate));
2080 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2081
2082 context->setGpsData(data);
2083}
2084
2085static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2086 jint height) {
2087 ALOGV("%s:", __FUNCTION__);
2088
2089 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2090 if (context == nullptr) {
2091 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2092 jniThrowException(env, "java/lang/AssertionError",
2093 "setThumbnail called with uninitialized DngCreator");
2094 return;
2095 }
2096
2097 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2098 jlong capacity = env->GetDirectBufferCapacity(buffer);
2099 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2100 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2101 "Invalid size %d for thumbnail, expected size was %d",
2102 capacity, fullSize);
2103 return;
2104 }
2105
2106 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2107 if (pixelBytes == nullptr) {
2108 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2109 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2110 return;
2111 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002112
2113 if (!context->setThumbnail(pixelBytes, width, height)) {
2114 jniThrowException(env, "java/lang/IllegalStateException",
2115 "Failed to set thumbnail.");
2116 return;
2117 }
2118}
2119
2120// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002121static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002122 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2123 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002124 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002125 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2126 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2127 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002128 uint32_t rStride = static_cast<uint32_t>(rowStride);
2129 uint32_t pStride = static_cast<uint32_t>(pixStride);
2130 uint32_t uWidth = static_cast<uint32_t>(width);
2131 uint32_t uHeight = static_cast<uint32_t>(height);
2132 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002133
2134 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2135 if(env->ExceptionCheck()) {
2136 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2137 return;
2138 }
2139
Ruben Brunk47e91f22014-05-28 18:38:42 -07002140 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002141 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002142 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2143 jniThrowException(env, "java/lang/AssertionError",
2144 "Write called with uninitialized DngCreator");
2145 return;
2146 }
Ruben Brunk20796122015-07-21 17:51:54 -07002147 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002148
Ruben Brunk20796122015-07-21 17:51:54 -07002149 if (writer.get() == nullptr) {
2150 return;
2151 }
2152
2153 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002154 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002155 return;
2156 }
2157
Ruben Brunk47e91f22014-05-28 18:38:42 -07002158 sp<JniInputByteBuffer> inBuf;
2159 Vector<StripSource*> sources;
2160 sp<DirectStripSource> thumbnailSource;
2161 uint32_t targetIfd = TIFF_IFD_0;
2162
2163 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2164
2165 if (hasThumbnail) {
2166 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2167 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2168 uint32_t thumbWidth = context->getThumbnailWidth();
2169 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2170 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2171 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2172 SAMPLES_PER_RGB_PIXEL);
2173 sources.add(thumbnailSource.get());
2174 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002175 }
2176
Ruben Brunk47e91f22014-05-28 18:38:42 -07002177 if (isDirect) {
2178 size_t fullSize = rStride * uHeight;
2179 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2180 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2181 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2182 "Invalid size %d for Image, size given in metadata is %d at current stride",
2183 capacity, fullSize);
2184 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002185 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002186
Ruben Brunk47e91f22014-05-28 18:38:42 -07002187 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002188 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002189 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2190 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2191 return;
2192 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002193
Ruben Brunk47e91f22014-05-28 18:38:42 -07002194 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2195 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2196 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2197 sources.add(&stripSource);
2198
2199 status_t ret = OK;
2200 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2201 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002202 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002203 jniThrowExceptionFmt(env, "java/io/IOException",
2204 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002205 }
2206 return;
2207 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002208 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002209 inBuf = new JniInputByteBuffer(env, inBuffer);
2210
2211 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2212 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2213 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2214 sources.add(&stripSource);
2215
2216 status_t ret = OK;
2217 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2218 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2219 if (!env->ExceptionCheck()) {
2220 jniThrowExceptionFmt(env, "java/io/IOException",
2221 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002222 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002223 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002224 }
2225 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002226}
2227
2228static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002229 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002230 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002231
2232 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2233 uint32_t pixStride = BYTES_PER_SAMPLE;
2234 uint32_t uWidth = static_cast<uint32_t>(width);
2235 uint32_t uHeight = static_cast<uint32_t>(height);
2236 uint64_t uOffset = static_cast<uint32_t>(offset);
2237
Dan Albert46d84442014-11-18 16:07:51 -08002238 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2239 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2240 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002241
2242 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002243 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002244 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2245 return;
2246 }
2247
Ruben Brunk47e91f22014-05-28 18:38:42 -07002248 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002249 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002250 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2251 jniThrowException(env, "java/lang/AssertionError",
2252 "Write called with uninitialized DngCreator");
2253 return;
2254 }
Ruben Brunk20796122015-07-21 17:51:54 -07002255 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002256
Ruben Brunk20796122015-07-21 17:51:54 -07002257 if (writer.get() == nullptr) {
2258 return;
2259 }
2260
2261 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002262 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002263 return;
2264 }
2265
2266 sp<DirectStripSource> thumbnailSource;
2267 uint32_t targetIfd = TIFF_IFD_0;
2268 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2269 Vector<StripSource*> sources;
2270
2271 if (hasThumbnail) {
2272 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2273 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2274 uint32_t width = context->getThumbnailWidth();
2275 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2276 width, context->getThumbnailHeight(), bytesPerPixel,
2277 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2278 SAMPLES_PER_RGB_PIXEL);
2279 sources.add(thumbnailSource.get());
2280 targetIfd = TIFF_IFD_SUB1;
2281 }
2282
2283 sp<JniInputStream> in = new JniInputStream(env, inStream);
2284
2285 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2286 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2287 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2288 sources.add(&stripSource);
2289
2290 status_t ret = OK;
2291 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2292 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2293 if (!env->ExceptionCheck()) {
2294 jniThrowExceptionFmt(env, "java/io/IOException",
2295 "Encountered error %d while writing file.", ret);
2296 }
2297 return;
2298 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002299}
2300
2301} /*extern "C" */
2302
Daniel Micay76f6a862015-09-19 17:31:01 -04002303static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002304 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2305 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002306 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2307 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002308 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2309 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002310 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2311 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2312 (void*) DngCreator_nativeSetGpsTags},
2313 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2314 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002315 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002316 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002317 (void*) DngCreator_nativeWriteInputStream},
2318};
2319
Ruben Brunkb6079002014-05-22 12:33:54 -07002320int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002321 return RegisterMethodsOrDie(env,
2322 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002323}