blob: e10a644ea6e14fce7d7bdd7ba8188df2e649d42e [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
Ruben Brunk20796122015-07-21 17:51:54 -070017#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
Ruben Brunk20796122015-07-21 17:51:54 -070083
Ruben Brunkb6079002014-05-22 12:33:54 -070084#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070085
86static struct {
87 jfieldID mNativeContext;
88} gDngCreatorClassInfo;
89
90static struct {
91 jmethodID mWriteMethod;
92} gOutputStreamClassInfo;
93
Ruben Brunk47e91f22014-05-28 18:38:42 -070094static struct {
95 jmethodID mReadMethod;
96 jmethodID mSkipMethod;
97} gInputStreamClassInfo;
98
99static struct {
100 jmethodID mGetMethod;
101} gInputByteBufferClassInfo;
102
Ruben Brunkf967a542014-04-28 16:31:11 -0700103enum {
104 BITS_PER_SAMPLE = 16,
105 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700106 BYTES_PER_RGB_PIXEL = 3,
107 BITS_PER_RGB_SAMPLE = 8,
108 BYTES_PER_RGB_SAMPLE = 1,
109 SAMPLES_PER_RGB_PIXEL = 3,
110 SAMPLES_PER_RAW_PIXEL = 1,
111 TIFF_IFD_0 = 0,
112 TIFF_IFD_SUB1 = 1,
113 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700114};
115
Ruben Brunk20796122015-07-21 17:51:54 -0700116
117/**
118 * POD container class for GPS tag data.
119 */
120class GpsData {
121public:
122 enum {
123 GPS_VALUE_LENGTH = 6,
124 GPS_REF_LENGTH = 2,
125 GPS_DATE_LENGTH = 11,
126 };
127
128 uint32_t mLatitude[GPS_VALUE_LENGTH];
129 uint32_t mLongitude[GPS_VALUE_LENGTH];
130 uint32_t mTimestamp[GPS_VALUE_LENGTH];
131 uint8_t mLatitudeRef[GPS_REF_LENGTH];
132 uint8_t mLongitudeRef[GPS_REF_LENGTH];
133 uint8_t mDate[GPS_DATE_LENGTH];
134};
135
Ruben Brunkf967a542014-04-28 16:31:11 -0700136// ----------------------------------------------------------------------------
137
Ruben Brunk47e91f22014-05-28 18:38:42 -0700138/**
139 * Container class for the persistent native context.
140 */
141
142class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700143public:
Ruben Brunk20796122015-07-21 17:51:54 -0700144 enum {
145 DATETIME_COUNT = 20,
146 };
147
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700148 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700149 virtual ~NativeContext();
150
151 TiffWriter* getWriter();
152
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700153 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
154 std::shared_ptr<const CameraMetadata> getResult() const;
155
Ruben Brunk20796122015-07-21 17:51:54 -0700156 uint32_t getThumbnailWidth() const;
157 uint32_t getThumbnailHeight() const;
158 const uint8_t* getThumbnail() const;
159 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700160
161 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
162
Ruben Brunk20796122015-07-21 17:51:54 -0700163 void setOrientation(uint16_t orientation);
164 uint16_t getOrientation() const;
165
166 void setDescription(const String8& desc);
167 String8 getDescription() const;
168 bool hasDescription() const;
169
170 void setGpsData(const GpsData& data);
171 GpsData getGpsData() const;
172 bool hasGpsData() const;
173
174 void setCaptureTime(const String8& formattedCaptureTime);
175 String8 getCaptureTime() const;
176 bool hasCaptureTime() const;
177
Ruben Brunk47e91f22014-05-28 18:38:42 -0700178private:
179 Vector<uint8_t> mCurrentThumbnail;
180 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700181 std::shared_ptr<CameraMetadata> mCharacteristics;
182 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700183 uint32_t mThumbnailWidth;
184 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700185 uint16_t mOrientation;
186 bool mThumbnailSet;
187 bool mGpsSet;
188 bool mDescriptionSet;
189 bool mCaptureTimeSet;
190 String8 mDescription;
191 GpsData mGpsData;
192 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700193};
194
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700195NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
196 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
197 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Ruben Brunk20796122015-07-21 17:51:54 -0700198 mThumbnailHeight(0), mOrientation(0), mThumbnailSet(false), mGpsSet(false),
199 mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700200
201NativeContext::~NativeContext() {}
202
203TiffWriter* NativeContext::getWriter() {
204 return &mWriter;
205}
206
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700207std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
208 return mCharacteristics;
209}
210
211std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
212 return mResult;
213}
214
Ruben Brunk20796122015-07-21 17:51:54 -0700215uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700216 return mThumbnailWidth;
217}
218
Ruben Brunk20796122015-07-21 17:51:54 -0700219uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700220 return mThumbnailHeight;
221}
222
Ruben Brunk20796122015-07-21 17:51:54 -0700223const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700224 return mCurrentThumbnail.array();
225}
226
Ruben Brunk20796122015-07-21 17:51:54 -0700227bool NativeContext::hasThumbnail() const {
228 return mThumbnailSet;
229}
230
Ruben Brunk47e91f22014-05-28 18:38:42 -0700231bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
232 mThumbnailWidth = width;
233 mThumbnailHeight = height;
234
235 size_t size = BYTES_PER_RGB_PIXEL * width * height;
236 if (mCurrentThumbnail.resize(size) < 0) {
237 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
238 return false;
239 }
240
241 uint8_t* thumb = mCurrentThumbnail.editArray();
242 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700243 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700244 return true;
245}
246
Ruben Brunk20796122015-07-21 17:51:54 -0700247void NativeContext::setOrientation(uint16_t orientation) {
248 mOrientation = orientation;
249}
250
251uint16_t NativeContext::getOrientation() const {
252 return mOrientation;
253}
254
255void NativeContext::setDescription(const String8& desc) {
256 mDescription = desc;
257 mDescriptionSet = true;
258}
259
260String8 NativeContext::getDescription() const {
261 return mDescription;
262}
263
264bool NativeContext::hasDescription() const {
265 return mDescriptionSet;
266}
267
268void NativeContext::setGpsData(const GpsData& data) {
269 mGpsData = data;
270 mGpsSet = true;
271}
272
273GpsData NativeContext::getGpsData() const {
274 return mGpsData;
275}
276
277bool NativeContext::hasGpsData() const {
278 return mGpsSet;
279}
280
281void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
282 mFormattedCaptureTime = formattedCaptureTime;
283 mCaptureTimeSet = true;
284}
285
286String8 NativeContext::getCaptureTime() const {
287 return mFormattedCaptureTime;
288}
289
290bool NativeContext::hasCaptureTime() const {
291 return mCaptureTimeSet;
292}
293
Ruben Brunk47e91f22014-05-28 18:38:42 -0700294// End of NativeContext
295// ----------------------------------------------------------------------------
296
297/**
298 * Wrapper class for a Java OutputStream.
299 *
300 * This class is not intended to be used across JNI calls.
301 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700302class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
303public:
304 JniOutputStream(JNIEnv* env, jobject outStream);
305
306 virtual ~JniOutputStream();
307
308 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700309
Ruben Brunkf967a542014-04-28 16:31:11 -0700310 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700311
Ruben Brunkf967a542014-04-28 16:31:11 -0700312 status_t close();
313private:
314 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700315 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700316 };
317 jobject mOutputStream;
318 JNIEnv* mEnv;
319 jbyteArray mByteArray;
320};
321
322JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
323 mEnv(env) {
324 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700325 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700326 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
327 }
328}
329
330JniOutputStream::~JniOutputStream() {
331 mEnv->DeleteLocalRef(mByteArray);
332}
333
334status_t JniOutputStream::open() {
335 // Do nothing
336 return OK;
337}
338
339status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
340 while(count > 0) {
341 size_t len = BYTE_ARRAY_LENGTH;
342 len = (count > len) ? len : count;
343 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
344
345 if (mEnv->ExceptionCheck()) {
346 return BAD_VALUE;
347 }
348
349 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
350 0, len);
351
352 if (mEnv->ExceptionCheck()) {
353 return BAD_VALUE;
354 }
355
356 count -= len;
357 offset += len;
358 }
359 return OK;
360}
361
362status_t JniOutputStream::close() {
363 // Do nothing
364 return OK;
365}
366
Ruben Brunk47e91f22014-05-28 18:38:42 -0700367// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700368// ----------------------------------------------------------------------------
369
Ruben Brunk47e91f22014-05-28 18:38:42 -0700370/**
371 * Wrapper class for a Java InputStream.
372 *
373 * This class is not intended to be used across JNI calls.
374 */
375class JniInputStream : public Input, public LightRefBase<JniInputStream> {
376public:
377 JniInputStream(JNIEnv* env, jobject inStream);
378
379 status_t open();
380
381 status_t close();
382
383 ssize_t read(uint8_t* buf, size_t offset, size_t count);
384
385 ssize_t skip(size_t count);
386
387 virtual ~JniInputStream();
388private:
389 enum {
390 BYTE_ARRAY_LENGTH = 4096
391 };
392 jobject mInStream;
393 JNIEnv* mEnv;
394 jbyteArray mByteArray;
395
396};
397
398JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
399 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700400 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700401 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
402 }
403}
404
405JniInputStream::~JniInputStream() {
406 mEnv->DeleteLocalRef(mByteArray);
407}
408
409ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
410
411 jint realCount = BYTE_ARRAY_LENGTH;
412 if (count < BYTE_ARRAY_LENGTH) {
413 realCount = count;
414 }
415 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
416 realCount);
417
418 if (actual < 0) {
419 return NOT_ENOUGH_DATA;
420 }
421
422 if (mEnv->ExceptionCheck()) {
423 return BAD_VALUE;
424 }
425
426 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
427 if (mEnv->ExceptionCheck()) {
428 return BAD_VALUE;
429 }
430 return actual;
431}
432
433ssize_t JniInputStream::skip(size_t count) {
434 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
435 static_cast<jlong>(count));
436
437 if (mEnv->ExceptionCheck()) {
438 return BAD_VALUE;
439 }
440 if (actual < 0) {
441 return NOT_ENOUGH_DATA;
442 }
443 return actual;
444}
445
446status_t JniInputStream::open() {
447 // Do nothing
448 return OK;
449}
450
451status_t JniInputStream::close() {
452 // Do nothing
453 return OK;
454}
455
456// End of JniInputStream
457// ----------------------------------------------------------------------------
458
459/**
460 * Wrapper class for a non-direct Java ByteBuffer.
461 *
462 * This class is not intended to be used across JNI calls.
463 */
464class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
465public:
466 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
467
468 status_t open();
469
470 status_t close();
471
472 ssize_t read(uint8_t* buf, size_t offset, size_t count);
473
474 virtual ~JniInputByteBuffer();
475private:
476 enum {
477 BYTE_ARRAY_LENGTH = 4096
478 };
479 jobject mInBuf;
480 JNIEnv* mEnv;
481 jbyteArray mByteArray;
482};
483
484JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
485 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700486 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700487 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
488 }
489}
490
491JniInputByteBuffer::~JniInputByteBuffer() {
492 mEnv->DeleteLocalRef(mByteArray);
493}
494
495ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
496 jint realCount = BYTE_ARRAY_LENGTH;
497 if (count < BYTE_ARRAY_LENGTH) {
498 realCount = count;
499 }
500
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700501 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
502 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800503 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700504
505 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800506 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700507 return BAD_VALUE;
508 }
509
510 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
511 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800512 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700513 return BAD_VALUE;
514 }
515 return realCount;
516}
517
518status_t JniInputByteBuffer::open() {
519 // Do nothing
520 return OK;
521}
522
523status_t JniInputByteBuffer::close() {
524 // Do nothing
525 return OK;
526}
527
528// End of JniInputByteBuffer
529// ----------------------------------------------------------------------------
530
531/**
532 * StripSource subclass for Input types.
533 *
534 * This class is not intended to be used across JNI calls.
535 */
536
537class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
538public:
539 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
540 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
541 uint32_t samplesPerPixel);
542
543 virtual ~InputStripSource();
544
545 virtual status_t writeToStream(Output& stream, uint32_t count);
546
547 virtual uint32_t getIfd() const;
548protected:
549 uint32_t mIfd;
550 Input* mInput;
551 uint32_t mWidth;
552 uint32_t mHeight;
553 uint32_t mPixStride;
554 uint32_t mRowStride;
555 uint64_t mOffset;
556 JNIEnv* mEnv;
557 uint32_t mBytesPerSample;
558 uint32_t mSamplesPerPixel;
559};
560
561InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
562 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
563 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
564 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
565 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
566 mSamplesPerPixel(samplesPerPixel) {}
567
568InputStripSource::~InputStripSource() {}
569
570status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700571 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700572 jlong offset = mOffset;
573
574 if (fullSize != count) {
575 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
576 fullSize);
577 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
578 return BAD_VALUE;
579 }
580
581 // Skip offset
582 while (offset > 0) {
583 ssize_t skipped = mInput->skip(offset);
584 if (skipped <= 0) {
585 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
586 jniThrowExceptionFmt(mEnv, "java/io/IOException",
587 "Early EOF encountered in skip, not enough pixel data for image of size %u",
588 fullSize);
589 skipped = NOT_ENOUGH_DATA;
590 } else {
591 if (!mEnv->ExceptionCheck()) {
592 jniThrowException(mEnv, "java/io/IOException",
593 "Error encountered while skip bytes in input stream.");
594 }
595 }
596
597 return skipped;
598 }
599 offset -= skipped;
600 }
601
602 Vector<uint8_t> row;
603 if (row.resize(mRowStride) < 0) {
604 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
605 return BAD_VALUE;
606 }
607
608 uint8_t* rowBytes = row.editArray();
609
610 for (uint32_t i = 0; i < mHeight; ++i) {
611 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800612 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700613
614 while (rowFillAmt < mRowStride) {
615 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
616 if (bytesRead <= 0) {
617 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800618 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
619 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700620 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800621 "Early EOF encountered, not enough pixel data for image of size %"
622 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700623 bytesRead = NOT_ENOUGH_DATA;
624 } else {
625 if (!mEnv->ExceptionCheck()) {
626 jniThrowException(mEnv, "java/io/IOException",
627 "Error encountered while reading");
628 }
629 }
630 return bytesRead;
631 }
632 rowFillAmt += bytesRead;
633 rowSize -= bytesRead;
634 }
635
636 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
637 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
638
639 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
640 mEnv->ExceptionCheck()) {
641 if (!mEnv->ExceptionCheck()) {
642 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
643 }
644 return BAD_VALUE;
645 }
646 } else {
647 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
648 jniThrowException(mEnv, "java/lang/IllegalStateException",
649 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
650 return BAD_VALUE;
651
652 // TODO: Add support for non-contiguous pixels if needed.
653 }
654 }
655 return OK;
656}
657
658uint32_t InputStripSource::getIfd() const {
659 return mIfd;
660}
661
662// End of InputStripSource
663// ----------------------------------------------------------------------------
664
665/**
666 * StripSource subclass for direct buffer types.
667 *
668 * This class is not intended to be used across JNI calls.
669 */
670
671class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
672public:
673 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
674 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
675 uint32_t bytesPerSample, uint32_t samplesPerPixel);
676
677 virtual ~DirectStripSource();
678
679 virtual status_t writeToStream(Output& stream, uint32_t count);
680
681 virtual uint32_t getIfd() const;
682protected:
683 uint32_t mIfd;
684 const uint8_t* mPixelBytes;
685 uint32_t mWidth;
686 uint32_t mHeight;
687 uint32_t mPixStride;
688 uint32_t mRowStride;
689 uint16_t mOffset;
690 JNIEnv* mEnv;
691 uint32_t mBytesPerSample;
692 uint32_t mSamplesPerPixel;
693};
694
695DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
696 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
697 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
698 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
699 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
700 mSamplesPerPixel(samplesPerPixel) {}
701
702DirectStripSource::~DirectStripSource() {}
703
704status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700705 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700706
707 if (fullSize != count) {
708 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
709 fullSize);
710 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
711 return BAD_VALUE;
712 }
713
Ruben Brunk20796122015-07-21 17:51:54 -0700714
Ruben Brunk47e91f22014-05-28 18:38:42 -0700715 if (mPixStride == mBytesPerSample * mSamplesPerPixel
716 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
717 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
718
719 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
720 if (!mEnv->ExceptionCheck()) {
721 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
722 }
723 return BAD_VALUE;
724 }
725 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
726 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
727
728 for (size_t i = 0; i < mHeight; ++i) {
729 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
730 mEnv->ExceptionCheck()) {
731 if (!mEnv->ExceptionCheck()) {
732 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
733 }
734 return BAD_VALUE;
735 }
736 }
737 } else {
738 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
739
740 jniThrowException(mEnv, "java/lang/IllegalStateException",
741 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
742 return BAD_VALUE;
743
744 // TODO: Add support for non-contiguous pixels if needed.
745 }
746 return OK;
747
748}
749
750uint32_t DirectStripSource::getIfd() const {
751 return mIfd;
752}
753
754// End of DirectStripSource
755// ----------------------------------------------------------------------------
756
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700757/**
Ruben Brunk20796122015-07-21 17:51:54 -0700758 * Given a buffer crop rectangle relative to the pixel array size, and the pre-correction active
759 * array crop rectangle for the camera characteristics, set the default crop rectangle in the
760 * TiffWriter relative to the buffer crop rectangle origin.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700761 */
762static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunk20796122015-07-21 17:51:54 -0700763 uint32_t bufWidth, uint32_t bufHeight, sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700764
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700765 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700766 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700767 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
768 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
769 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
770 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
771
Ruben Brunk20796122015-07-21 17:51:54 -0700772 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
773
774 // Crop based on pre-correction array for pixel array
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700775 uint32_t aLeft = xmin;
776 uint32_t aTop = ymin;
777 uint32_t aRight = xmin + width;
778 uint32_t aBottom = ymin + height;
779
Ruben Brunk20796122015-07-21 17:51:54 -0700780 // 8 pixel border crop for pixel array dimens
781 uint32_t bLeft = margin;
782 uint32_t bTop = margin;
783 uint32_t bRight = bufWidth - margin;
784 uint32_t bBottom = bufHeight - margin;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700785
Ruben Brunk20796122015-07-21 17:51:54 -0700786 // Set the crop to be the intersection of the two rectangles
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700787 uint32_t defaultCropOrigin[] = {std::max(aLeft, bLeft), std::max(aTop, bTop)};
788 uint32_t defaultCropSize[] = {std::min(aRight, bRight) - defaultCropOrigin[0],
789 std::min(aBottom, bBottom) - defaultCropOrigin[1]};
790
Ruben Brunk20796122015-07-21 17:51:54 -0700791 // If using buffers with pre-correction array dimens, switch to 8 pixel border crop
792 // relative to the pixel array dimens
793 if (bufWidth == width && bufHeight == height) {
794 defaultCropOrigin[0] = xmin + margin;
795 defaultCropOrigin[1] = ymin + margin;
796 defaultCropSize[0] = width - margin;
797 defaultCropSize[1] = height - margin;
798 }
799
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700800 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
801 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
802 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
803 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
804
805 return OK;
806}
807
Ruben Brunk20796122015-07-21 17:51:54 -0700808static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700809 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700810 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700811 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700812 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700813 return false;
814 }
815
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700816 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700817 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700818 "Image height %d is invalid", height);
819 return false;
820 }
821
822 camera_metadata_ro_entry preCorrectionEntry =
823 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
824 camera_metadata_ro_entry pixelArrayEntry =
825 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
826
827 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
828 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
829 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
830 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
831
832 bool matchesPixelArray = (pWidth == width && pHeight == height);
833 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
834
Ruben Brunk20796122015-07-21 17:51:54 -0700835 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700836 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
837 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
838 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
839 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700840 return false;
841 }
842
843 return true;
844}
845
Ruben Brunk20796122015-07-21 17:51:54 -0700846static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700847 const Vector<uint16_t>& entries) {
848 for (size_t i = 0; i < entries.size(); ++i) {
849 uint16_t tagId = entries[i];
850 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700851 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700852 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
853 ifdFrom);
854 return BAD_VALUE;
855 }
856 if (writer->addEntry(entry, ifdTo) != OK) {
857 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
858 ifdFrom);
859 return BAD_VALUE;
860 }
861 writer->removeEntry(tagId, ifdFrom);
862 }
863 return OK;
864}
865
Ruben Brunkd70132c2014-08-22 16:24:49 -0700866/**
867 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
868 * Returns OK on success, or a negative error code if the CFA enum was invalid.
869 */
870static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
871 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
872 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
873 cfaEnum);
874 switch(cfa) {
875 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
876 cfaOut[0] = 0;
877 cfaOut[1] = 1;
878 cfaOut[2] = 1;
879 cfaOut[3] = 2;
880 break;
881 }
882 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
883 cfaOut[0] = 1;
884 cfaOut[1] = 0;
885 cfaOut[2] = 2;
886 cfaOut[3] = 1;
887 break;
888 }
889 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
890 cfaOut[0] = 1;
891 cfaOut[1] = 2;
892 cfaOut[2] = 0;
893 cfaOut[3] = 1;
894 break;
895 }
896 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
897 cfaOut[0] = 2;
898 cfaOut[1] = 1;
899 cfaOut[2] = 1;
900 cfaOut[3] = 0;
901 break;
902 }
903 default: {
904 return BAD_VALUE;
905 }
906 }
907 return OK;
908}
909
910/**
911 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
912 * RGGB for an unknown enum.
913 */
914static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
915 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
916 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
917 cfaEnum);
918 switch(cfa) {
919 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
920 return OpcodeListBuilder::CFA_RGGB;
921 }
922 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
923 return OpcodeListBuilder::CFA_GRBG;
924 }
925 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
926 return OpcodeListBuilder::CFA_GBRG;
927 }
928 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
929 return OpcodeListBuilder::CFA_BGGR;
930 }
931 default: {
932 return OpcodeListBuilder::CFA_RGGB;
933 }
934 }
935}
936
937/**
938 * For each color plane, find the corresponding noise profile coefficients given in the
939 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
940 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
941 *
942 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
943 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
944 * coefficients.
945 * numChannels - the number of noise profile coefficient pairs and color channels given in
946 * the perChannelNoiseProfile and cfa arguments, respectively.
947 * planeColors - the color planes in the noise profile output.
948 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
949 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
950 *
951 * returns OK, or a negative error code on failure.
952 */
953static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
954 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
955 /*out*/double* noiseProfile) {
956
957 for (size_t p = 0; p < numPlanes; ++p) {
958 size_t S = p * 2;
959 size_t O = p * 2 + 1;
960
961 noiseProfile[S] = 0;
962 noiseProfile[O] = 0;
963 bool uninitialized = true;
964 for (size_t c = 0; c < numChannels; ++c) {
965 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
966 noiseProfile[S] = perChannelNoiseProfile[c * 2];
967 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
968 uninitialized = false;
969 }
970 }
971 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800972 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
973 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700974 return BAD_VALUE;
975 }
976 }
977 return OK;
978}
979
Ruben Brunk47e91f22014-05-28 18:38:42 -0700980// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700981extern "C" {
982
Ruben Brunk47e91f22014-05-28 18:38:42 -0700983static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700984 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700985 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700986 gDngCreatorClassInfo.mNativeContext));
987}
988
Ruben Brunk47e91f22014-05-28 18:38:42 -0700989static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700990 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700991 NativeContext* current = DngCreator_getNativeContext(env, thiz);
992
Ruben Brunk20796122015-07-21 17:51:54 -0700993 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700994 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700995 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700996
Ruben Brunkf967a542014-04-28 16:31:11 -0700997 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700998 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700999 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001000
Ruben Brunkf967a542014-04-28 16:31:11 -07001001 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001002 reinterpret_cast<jlong>(context.get()));
1003}
1004
Ruben Brunkf967a542014-04-28 16:31:11 -07001005static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
1006 ALOGV("%s:", __FUNCTION__);
1007
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001008 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1009 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001010
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001011 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1012 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1013 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001014
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001015 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1016 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1017 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001018
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001019 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1020 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1021 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001022}
1023
1024static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001025 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001026 ALOGV("%s:", __FUNCTION__);
1027 CameraMetadata characteristics;
1028 CameraMetadata results;
1029 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1030 jniThrowException(env, "java/lang/AssertionError",
1031 "No native metadata defined for camera characteristics.");
1032 return;
1033 }
1034 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1035 jniThrowException(env, "java/lang/AssertionError",
1036 "No native metadata defined for capture results.");
1037 return;
1038 }
1039
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001040 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001041
1042 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1043
1044 size_t len = strlen(captureTime) + 1;
1045 if (len != NativeContext::DATETIME_COUNT) {
1046 jniThrowException(env, "java/lang/IllegalArgumentException",
1047 "Formatted capture time string length is not required 20 characters");
1048 return;
1049 }
1050
1051 nativeContext->setCaptureTime(String8(captureTime));
1052
1053 DngCreator_setNativeContext(env, thiz, nativeContext);
1054}
1055
1056static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1057 uint32_t imageHeight) {
1058
1059 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1060
1061 if (nativeContext == nullptr) {
1062 jniThrowException(env, "java/lang/AssertionError",
1063 "No native context, must call init before other operations.");
1064 return nullptr;
1065 }
1066
1067 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1068 CameraMetadata results = *(nativeContext->getResult());
1069
1070 sp<TiffWriter> writer = new TiffWriter();
1071
1072 uint32_t preWidth = 0;
1073 uint32_t preHeight = 0;
1074 {
1075 // Check dimensions
1076 camera_metadata_entry entry =
1077 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1078 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1079 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1080 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1081
1082 camera_metadata_entry pixelArrayEntry =
1083 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1084 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1085 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1086
1087 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1088 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1089 jniThrowException(env, "java/lang/AssertionError",
1090 "Height and width of imate buffer did not match height and width of"
1091 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1092 return nullptr;
1093 }
1094 }
1095
1096
Ruben Brunkf967a542014-04-28 16:31:11 -07001097
1098 writer->addIfd(TIFF_IFD_0);
1099
1100 status_t err = OK;
1101
1102 const uint32_t samplesPerPixel = 1;
1103 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001104
1105 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001106 uint8_t cfaPlaneColor[3] = {0, 1, 2};
1107 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001108
1109 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001110 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001111
1112 // Setup main image tags
1113
Ruben Brunkf967a542014-04-28 16:31:11 -07001114 {
1115 // Set orientation
1116 uint16_t orientation = 1; // Normal
Ruben Brunk20796122015-07-21 17:51:54 -07001117 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1118 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001119 }
1120
1121 {
1122 // Set subfiletype
1123 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001124 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1125 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001126 }
1127
1128 {
1129 // Set bits per sample
1130 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001131 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001132 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001133 }
1134
1135 {
1136 // Set compression
1137 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001138 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1139 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001140 }
1141
1142 {
1143 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001144 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1145 env, TAG_IMAGEWIDTH, writer);
1146 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1147 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001148 }
1149
1150 {
1151 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -07001152 uint16_t interpretation = 32803; // CFA
Ruben Brunk20796122015-07-21 17:51:54 -07001153 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1154 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001155 }
1156
1157 {
1158 // Set blacklevel tags
1159 camera_metadata_entry entry =
1160 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk20796122015-07-21 17:51:54 -07001161 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001162 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
Ruben Brunk20796122015-07-21 17:51:54 -07001163 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel,
1164 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001165
1166 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001167 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1168 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001169 }
1170
1171 {
1172 // Set samples per pixel
1173 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001174 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001175 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001176 }
1177
1178 {
1179 // Set planar configuration
1180 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001181 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1182 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001183 }
1184
1185 {
1186 // Set CFA pattern dimensions
1187 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001188 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1189 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001190 }
1191
1192 {
1193 // Set CFA pattern
1194 camera_metadata_entry entry =
1195 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk20796122015-07-21 17:51:54 -07001196 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001197
1198 const int cfaLength = 4;
1199 cfaEnum = entry.data.u8[0];
1200 uint8_t cfa[cfaLength];
1201 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1202 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1203 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001204 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001205
Ruben Brunk20796122015-07-21 17:51:54 -07001206 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1207 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001208
1209 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001210 }
1211
1212 {
1213 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001214 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1215 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001216 }
1217
1218 {
1219 // Set CFA layout
1220 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001221 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001222 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001223 }
1224
1225 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001226 // image description
1227 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001228 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1229 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001230 }
1231
1232 {
1233 // make
1234 char manufacturer[PROPERTY_VALUE_MAX];
1235
1236 // Use "" to represent unknown make as suggested in TIFF/EP spec.
1237 property_get("ro.product.manufacturer", manufacturer, "");
1238 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1239
Ruben Brunk20796122015-07-21 17:51:54 -07001240 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
1241 reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001242 }
1243
1244 {
1245 // model
1246 char model[PROPERTY_VALUE_MAX];
1247
1248 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1249 property_get("ro.product.model", model, "");
1250 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1251
Ruben Brunk20796122015-07-21 17:51:54 -07001252 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
1253 reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001254 }
1255
1256 {
1257 // x resolution
1258 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001259 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001260 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001261
1262 // y resolution
1263 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001264 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001265 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001266
1267 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001268 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001269 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001270 }
1271
1272 {
1273 // software
1274 char software[PROPERTY_VALUE_MAX];
1275 property_get("ro.build.fingerprint", software, "");
1276 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001277 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
1278 reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001279 }
1280
Ruben Brunk20796122015-07-21 17:51:54 -07001281 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001282 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001283 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001284
Ruben Brunk20796122015-07-21 17:51:54 -07001285 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1286 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001287 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1288 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001289 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001290 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001291
1292 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001293 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1294 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001295 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1296 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001297 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001298 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001299 }
1300
1301 {
1302 // TIFF/EP standard id
1303 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001304 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001305 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001306 }
1307
1308 {
1309 // copyright
1310 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001311 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001312 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001313 }
1314
1315 {
1316 // exposure time
1317 camera_metadata_entry entry =
1318 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001319 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001320
1321 int64_t exposureTime = *(entry.data.i64);
1322
1323 if (exposureTime < 0) {
1324 // Should be unreachable
1325 jniThrowException(env, "java/lang/IllegalArgumentException",
1326 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001327 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001328 }
1329
1330 // Ensure exposure time doesn't overflow (for exposures > 4s)
1331 uint32_t denominator = 1000000000;
1332 while (exposureTime > UINT32_MAX) {
1333 exposureTime >>= 1;
1334 denominator >>= 1;
1335 if (denominator == 0) {
1336 // Should be unreachable
1337 jniThrowException(env, "java/lang/IllegalArgumentException",
1338 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001339 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001340 }
1341 }
1342
1343 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001344 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001345 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001346
1347 }
1348
1349 {
1350 // ISO speed ratings
1351 camera_metadata_entry entry =
1352 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001353 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001354
1355 int32_t tempIso = *(entry.data.i32);
1356 if (tempIso < 0) {
1357 jniThrowException(env, "java/lang/IllegalArgumentException",
1358 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001359 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001360 }
1361
1362 if (tempIso > UINT16_MAX) {
1363 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1364 tempIso = UINT16_MAX;
1365 }
1366
1367 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001368 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001369 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001370 }
1371
1372 {
1373 // focal length
1374 camera_metadata_entry entry =
1375 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001376 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001377
1378 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001379 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001380 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001381 }
1382
1383 {
1384 // f number
1385 camera_metadata_entry entry =
1386 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001387 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001388
1389 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001390 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001391 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001392 }
1393
1394 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001395 // Set DNG version information
1396 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001397 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001398 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001399
1400 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001401 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1402 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001403 }
1404
1405 {
1406 // Set whitelevel
1407 camera_metadata_entry entry =
1408 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001409 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001410 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001411 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1412 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001413 }
1414
1415 {
1416 // Set default scale
1417 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001418 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1419 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001420 }
1421
1422 bool singleIlluminant = false;
1423 {
1424 // Set calibration illuminants
1425 camera_metadata_entry entry1 =
1426 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001427 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001428 camera_metadata_entry entry2 =
1429 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1430 if (entry2.count == 0) {
1431 singleIlluminant = true;
1432 }
1433 uint16_t ref1 = entry1.data.u8[0];
1434
Ruben Brunk20796122015-07-21 17:51:54 -07001435 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001436 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001437
1438 if (!singleIlluminant) {
1439 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001440 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001441 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001442 }
1443 }
1444
1445 {
1446 // Set color transforms
1447 camera_metadata_entry entry1 =
1448 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001449 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001450
1451 int32_t colorTransform1[entry1.count * 2];
1452
1453 size_t ctr = 0;
1454 for(size_t i = 0; i < entry1.count; ++i) {
1455 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1456 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1457 }
1458
Ruben Brunk20796122015-07-21 17:51:54 -07001459 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1460 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001461
1462 if (!singleIlluminant) {
1463 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001464 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001465 int32_t colorTransform2[entry2.count * 2];
1466
1467 ctr = 0;
1468 for(size_t i = 0; i < entry2.count; ++i) {
1469 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1470 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1471 }
1472
Ruben Brunk20796122015-07-21 17:51:54 -07001473 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1474 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001475 }
1476 }
1477
1478 {
1479 // Set calibration transforms
1480 camera_metadata_entry entry1 =
1481 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001482 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001483
1484 int32_t calibrationTransform1[entry1.count * 2];
1485
1486 size_t ctr = 0;
1487 for(size_t i = 0; i < entry1.count; ++i) {
1488 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1489 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1490 }
1491
Ruben Brunk20796122015-07-21 17:51:54 -07001492 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001493 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001494
1495 if (!singleIlluminant) {
1496 camera_metadata_entry entry2 =
1497 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001498 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001499 int32_t calibrationTransform2[entry2.count * 2];
1500
1501 ctr = 0;
1502 for(size_t i = 0; i < entry2.count; ++i) {
1503 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1504 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1505 }
1506
Ruben Brunk20796122015-07-21 17:51:54 -07001507 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001508 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001509 }
1510 }
1511
1512 {
1513 // Set forward transforms
1514 camera_metadata_entry entry1 =
1515 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001516 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001517
1518 int32_t forwardTransform1[entry1.count * 2];
1519
1520 size_t ctr = 0;
1521 for(size_t i = 0; i < entry1.count; ++i) {
1522 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1523 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1524 }
1525
Ruben Brunk20796122015-07-21 17:51:54 -07001526 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1527 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001528
1529 if (!singleIlluminant) {
1530 camera_metadata_entry entry2 =
1531 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001532 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001533 int32_t forwardTransform2[entry2.count * 2];
1534
1535 ctr = 0;
1536 for(size_t i = 0; i < entry2.count; ++i) {
1537 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1538 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1539 }
1540
Ruben Brunk20796122015-07-21 17:51:54 -07001541 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1542 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001543 }
1544 }
1545
1546 {
1547 // Set camera neutral
1548 camera_metadata_entry entry =
1549 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001550 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001551 uint32_t cameraNeutral[entry.count * 2];
1552
1553 size_t ctr = 0;
1554 for(size_t i = 0; i < entry.count; ++i) {
1555 cameraNeutral[ctr++] =
1556 static_cast<uint32_t>(entry.data.r[i].numerator);
1557 cameraNeutral[ctr++] =
1558 static_cast<uint32_t>(entry.data.r[i].denominator);
1559 }
1560
Ruben Brunk20796122015-07-21 17:51:54 -07001561 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001562 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001563 }
1564
Ruben Brunkf967a542014-04-28 16:31:11 -07001565
1566 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001567 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001568 if (calculateAndSetCrop(env, characteristics, imageWidth, imageHeight, writer) != OK) {
1569 return nullptr;
1570 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001571 camera_metadata_entry entry =
1572 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunk20796122015-07-21 17:51:54 -07001573 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001574 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1575 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1576 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1577 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001578
1579 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1580 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1581 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001582 }
1583
1584 {
1585 // Setup unique camera model tag
1586 char model[PROPERTY_VALUE_MAX];
1587 property_get("ro.product.model", model, "");
1588
1589 char manufacturer[PROPERTY_VALUE_MAX];
1590 property_get("ro.product.manufacturer", manufacturer, "");
1591
1592 char brand[PROPERTY_VALUE_MAX];
1593 property_get("ro.product.brand", brand, "");
1594
1595 String8 cameraModel(model);
1596 cameraModel += "-";
1597 cameraModel += manufacturer;
1598 cameraModel += "-";
1599 cameraModel += brand;
1600
Ruben Brunk20796122015-07-21 17:51:54 -07001601 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001602 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001603 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001604 }
1605
1606 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001607 // Setup sensor noise model
1608 camera_metadata_entry entry =
1609 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1610
Ruben Brunkd70132c2014-08-22 16:24:49 -07001611 const status_t numPlaneColors = 3;
1612 const status_t numCfaChannels = 4;
1613
1614 uint8_t cfaOut[numCfaChannels];
1615 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1616 jniThrowException(env, "java/lang/IllegalArgumentException",
1617 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001618 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001619 }
1620
1621 double noiseProfile[numPlaneColors * 2];
1622
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001623 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001624 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001625 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1626 "in characteristics, no noise profile tag written...",
1627 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001628 } else {
1629 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1630 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1631
Ruben Brunk20796122015-07-21 17:51:54 -07001632 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1633 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1634 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001635 } else {
1636 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1637 " tag written...", __FUNCTION__);
1638 }
1639 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001640 } else {
1641 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1642 __FUNCTION__);
1643 }
1644 }
1645
1646 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001647 // Set up opcode List 2
1648 OpcodeListBuilder builder;
1649 status_t err = OK;
1650
1651 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001652 camera_metadata_entry entry1 =
1653 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001654
1655 uint32_t lsmWidth = 0;
1656 uint32_t lsmHeight = 0;
1657
1658 if (entry1.count != 0) {
1659 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1660 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1661 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001662
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001663 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001664
Ruben Brunk20796122015-07-21 17:51:54 -07001665 camera_metadata_entry entry =
1666 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1667 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1668 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1669 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1670 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1671 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001672 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001673 err = builder.addGainMapsForMetadata(lsmWidth,
1674 lsmHeight,
Ruben Brunk20796122015-07-21 17:51:54 -07001675 ymin,
1676 xmin,
1677 height,
1678 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001679 opcodeCfaLayout,
1680 entry2.data.f);
1681 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001682 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1683 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001684 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001685 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001686 }
1687
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001688
1689 // Set up bad pixel correction list
1690 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
1691
1692 if ((entry3.count % 2) != 0) {
1693 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1694 __FUNCTION__);
1695 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1696 return nullptr;
1697 }
1698
1699 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1700 std::vector<uint32_t> v;
1701 for (size_t i = 0; i < entry3.count; i+=2) {
1702 int32_t x = entry3.data.i32[i];
1703 int32_t y = entry3.data.i32[i + 1];
1704 x -= static_cast<int32_t>(xmin);
1705 y -= static_cast<int32_t>(ymin);
1706 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1707 static_cast<uint32_t>(y) >= width) {
1708 continue;
1709 }
1710 v.push_back(x);
1711 v.push_back(y);
1712 }
1713 const uint32_t* badPixels = &v[0];
1714 uint32_t badPixelCount = v.size();
1715
1716 if (badPixelCount > 0) {
1717 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1718
1719 if (err != OK) {
1720 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1721 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1722 return nullptr;
1723 }
1724 }
1725
1726
Ruben Brunkfe816622015-06-16 23:03:09 -07001727 size_t listSize = builder.getSize();
1728 uint8_t opcodeListBuf[listSize];
1729 err = builder.buildOpList(opcodeListBuf);
1730 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001731 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001732 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1733 } else {
1734 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1735 "map.", __FUNCTION__);
1736 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1737 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001738 return nullptr;
Ruben Brunkfe816622015-06-16 23:03:09 -07001739 }
1740 }
1741
1742 {
1743 // Set up opcode List 3
1744 OpcodeListBuilder builder;
1745 status_t err = OK;
1746
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001747 // Set up rectilinear distortion correction
1748 camera_metadata_entry entry3 =
1749 results.find(ANDROID_LENS_RADIAL_DISTORTION);
1750 camera_metadata_entry entry4 =
1751 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1752
1753 if (entry3.count == 6 && entry4.count == 5) {
1754 float cx = entry4.data.f[/*c_x*/2];
1755 float cy = entry4.data.f[/*c_y*/3];
Ruben Brunk20796122015-07-21 17:51:54 -07001756 err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001757 cy);
1758 if (err != OK) {
1759 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1760 jniThrowRuntimeException(env, "failed to add distortion correction.");
Ruben Brunk20796122015-07-21 17:51:54 -07001761 return nullptr;
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001762 }
1763 }
1764
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001765 size_t listSize = builder.getSize();
1766 uint8_t opcodeListBuf[listSize];
1767 err = builder.buildOpList(opcodeListBuf);
1768 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001769 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001770 TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001771 } else {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001772 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1773 "map.", __FUNCTION__);
1774 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1775 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001776 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001777 }
1778 }
1779
Ruben Brunk20796122015-07-21 17:51:54 -07001780 {
1781 // Set up orientation tags.
1782 uint16_t orientation = nativeContext->getOrientation();
1783 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1784 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001785
Ruben Brunk47e91f22014-05-28 18:38:42 -07001786 }
1787
Ruben Brunk20796122015-07-21 17:51:54 -07001788 if (nativeContext->hasDescription()){
1789 // Set Description
1790 String8 description = nativeContext->getDescription();
1791 size_t len = description.bytes() + 1;
1792 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1793 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1794 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1795 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001796 }
1797 }
1798
Ruben Brunk20796122015-07-21 17:51:54 -07001799 if (nativeContext->hasGpsData()) {
1800 // Set GPS tags
1801 GpsData gpsData = nativeContext->getGpsData();
1802 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1803 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1804 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1805 TIFF_IFD_0);
1806 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1807 return nullptr;
1808 }
1809 }
1810
1811 {
1812 uint8_t version[] = {2, 3, 0, 0};
1813 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1814 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1815 }
1816
1817 {
1818 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1819 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1820 TAG_GPSLATITUDEREF, writer);
1821 }
1822
1823 {
1824 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1825 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1826 TAG_GPSLONGITUDEREF, writer);
1827 }
1828
1829 {
1830 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1831 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1832 }
1833
1834 {
1835 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1836 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1837 }
1838
1839 {
1840 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1841 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1842 }
1843
1844 {
1845 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1846 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1847 TAG_GPSDATESTAMP, writer);
1848 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001849 }
1850
Ruben Brunk47e91f22014-05-28 18:38:42 -07001851
Ruben Brunk20796122015-07-21 17:51:54 -07001852 if (nativeContext->hasThumbnail()) {
1853 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1854 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1855 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1856 TIFF_IFD_0);
1857 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1858 return nullptr;
1859 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001860 }
1861
1862 Vector<uint16_t> tagsToMove;
1863 tagsToMove.add(TAG_ORIENTATION);
1864 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001865 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001866 tagsToMove.add(TAG_BITSPERSAMPLE);
1867 tagsToMove.add(TAG_COMPRESSION);
1868 tagsToMove.add(TAG_IMAGEWIDTH);
1869 tagsToMove.add(TAG_IMAGELENGTH);
1870 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1871 tagsToMove.add(TAG_BLACKLEVEL);
1872 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1873 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1874 tagsToMove.add(TAG_PLANARCONFIGURATION);
1875 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1876 tagsToMove.add(TAG_CFAPATTERN);
1877 tagsToMove.add(TAG_CFAPLANECOLOR);
1878 tagsToMove.add(TAG_CFALAYOUT);
1879 tagsToMove.add(TAG_XRESOLUTION);
1880 tagsToMove.add(TAG_YRESOLUTION);
1881 tagsToMove.add(TAG_RESOLUTIONUNIT);
1882 tagsToMove.add(TAG_WHITELEVEL);
1883 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001884 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1885 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1886 tagsToMove.add(TAG_OPCODELIST2);
Ruben Brunkfe816622015-06-16 23:03:09 -07001887 tagsToMove.add(TAG_OPCODELIST3);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001888
1889 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1890 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07001891 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001892 }
1893
1894 // Make sure both IFDs get the same orientation tag
1895 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
Ruben Brunk20796122015-07-21 17:51:54 -07001896 if (orientEntry.get() != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001897 writer->addEntry(orientEntry, TIFF_IFD_0);
1898 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001899
Ruben Brunk20796122015-07-21 17:51:54 -07001900 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001901
Ruben Brunk20796122015-07-21 17:51:54 -07001902 {
1903 // Set photometric interpretation
1904 uint16_t interpretation = 2; // RGB
1905 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1906 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001907 }
Ruben Brunk20796122015-07-21 17:51:54 -07001908
1909 {
1910 // Set planar configuration
1911 uint16_t config = 1; // Chunky
1912 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1913 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
1914 }
1915
1916 {
1917 // Set samples per pixel
1918 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1919 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
1920 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
1921 }
1922
1923 {
1924 // Set bits per sample
1925 uint16_t bits = BITS_PER_RGB_SAMPLE;
1926 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0),
1927 env, TAG_BITSPERSAMPLE, writer);
1928 }
1929
1930 {
1931 // Set subfiletype
1932 uint32_t subfileType = 1; // Thumbnail image
1933 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1934 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
1935 }
1936
1937 {
1938 // Set compression
1939 uint16_t compression = 1; // None
1940 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1941 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
1942 }
1943
1944 {
1945 // Set dimensions
1946 uint32_t uWidth = nativeContext->getThumbnailWidth();
1947 uint32_t uHeight = nativeContext->getThumbnailHeight();
1948 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
1949 env, TAG_IMAGEWIDTH, writer);
1950 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
1951 env, TAG_IMAGELENGTH, writer);
1952 }
1953
1954 {
1955 // x resolution
1956 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1957 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1958 env, TAG_XRESOLUTION, writer);
1959
1960 // y resolution
1961 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1962 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1963 env, TAG_YRESOLUTION, writer);
1964
1965 uint16_t unit = 2; // inches
1966 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1967 env, TAG_RESOLUTIONUNIT, writer);
1968 }
1969 }
1970
1971 if (writer->addStrip(TIFF_IFD_0) != OK) {
1972 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1973 jniThrowException(env, "java/lang/IllegalStateException",
1974 "Failed to setup thumbnail strip tags.");
1975 return nullptr;
1976 }
1977
1978 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001979 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1980 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1981 jniThrowException(env, "java/lang/IllegalStateException",
1982 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07001983 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001984 }
1985 }
Ruben Brunk20796122015-07-21 17:51:54 -07001986 return writer;
1987}
1988
1989static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1990 ALOGV("%s:", __FUNCTION__);
1991 DngCreator_setNativeContext(env, thiz, nullptr);
1992}
1993
1994static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
1995 ALOGV("%s:", __FUNCTION__);
1996
1997 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1998 if (context == nullptr) {
1999 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2000 jniThrowException(env, "java/lang/AssertionError",
2001 "setOrientation called with uninitialized DngCreator");
2002 return;
2003 }
2004
2005 uint16_t orientation = static_cast<uint16_t>(orient);
2006 context->setOrientation(orientation);
2007}
2008
2009static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2010 ALOGV("%s:", __FUNCTION__);
2011
2012 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2013 if (context == nullptr) {
2014 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2015 jniThrowException(env, "java/lang/AssertionError",
2016 "setDescription called with uninitialized DngCreator");
2017 return;
2018 }
2019
2020 const char* desc = env->GetStringUTFChars(description, nullptr);
2021 context->setDescription(String8(desc));
2022 env->ReleaseStringUTFChars(description, desc);
2023}
2024
2025static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2026 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2027 ALOGV("%s:", __FUNCTION__);
2028
2029 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2030 if (context == nullptr) {
2031 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2032 jniThrowException(env, "java/lang/AssertionError",
2033 "setGpsTags called with uninitialized DngCreator");
2034 return;
2035 }
2036
2037 GpsData data;
2038
2039 jsize latLen = env->GetArrayLength(latTag);
2040 jsize longLen = env->GetArrayLength(longTag);
2041 jsize timeLen = env->GetArrayLength(timeTag);
2042 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2043 jniThrowException(env, "java/lang/IllegalArgumentException",
2044 "invalid latitude tag length");
2045 return;
2046 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2047 jniThrowException(env, "java/lang/IllegalArgumentException",
2048 "invalid longitude tag length");
2049 return;
2050 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2051 jniThrowException(env, "java/lang/IllegalArgumentException",
2052 "invalid time tag length");
2053 return;
2054 }
2055
2056 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2057 reinterpret_cast<jint*>(&data.mLatitude));
2058 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2059 reinterpret_cast<jint*>(&data.mLongitude));
2060 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2061 reinterpret_cast<jint*>(&data.mTimestamp));
2062
2063
2064 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2065 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2066 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2067 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2068 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2069 reinterpret_cast<char*>(&data.mDate));
2070 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2071
2072 context->setGpsData(data);
2073}
2074
2075static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2076 jint height) {
2077 ALOGV("%s:", __FUNCTION__);
2078
2079 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2080 if (context == nullptr) {
2081 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2082 jniThrowException(env, "java/lang/AssertionError",
2083 "setThumbnail called with uninitialized DngCreator");
2084 return;
2085 }
2086
2087 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2088 jlong capacity = env->GetDirectBufferCapacity(buffer);
2089 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2090 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2091 "Invalid size %d for thumbnail, expected size was %d",
2092 capacity, fullSize);
2093 return;
2094 }
2095
2096 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2097 if (pixelBytes == nullptr) {
2098 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2099 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2100 return;
2101 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002102
2103 if (!context->setThumbnail(pixelBytes, width, height)) {
2104 jniThrowException(env, "java/lang/IllegalStateException",
2105 "Failed to set thumbnail.");
2106 return;
2107 }
2108}
2109
2110// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002111static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002112 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2113 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002114 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002115 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2116 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2117 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002118 uint32_t rStride = static_cast<uint32_t>(rowStride);
2119 uint32_t pStride = static_cast<uint32_t>(pixStride);
2120 uint32_t uWidth = static_cast<uint32_t>(width);
2121 uint32_t uHeight = static_cast<uint32_t>(height);
2122 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002123
2124 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2125 if(env->ExceptionCheck()) {
2126 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2127 return;
2128 }
2129
Ruben Brunk47e91f22014-05-28 18:38:42 -07002130 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002131 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002132 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2133 jniThrowException(env, "java/lang/AssertionError",
2134 "Write called with uninitialized DngCreator");
2135 return;
2136 }
Ruben Brunk20796122015-07-21 17:51:54 -07002137 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002138
Ruben Brunk20796122015-07-21 17:51:54 -07002139 if (writer.get() == nullptr) {
2140 return;
2141 }
2142
2143 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002144 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002145 return;
2146 }
2147
Ruben Brunk47e91f22014-05-28 18:38:42 -07002148 sp<JniInputByteBuffer> inBuf;
2149 Vector<StripSource*> sources;
2150 sp<DirectStripSource> thumbnailSource;
2151 uint32_t targetIfd = TIFF_IFD_0;
2152
2153 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2154
2155 if (hasThumbnail) {
2156 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2157 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2158 uint32_t thumbWidth = context->getThumbnailWidth();
2159 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2160 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2161 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2162 SAMPLES_PER_RGB_PIXEL);
2163 sources.add(thumbnailSource.get());
2164 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002165 }
2166
Ruben Brunk47e91f22014-05-28 18:38:42 -07002167 if (isDirect) {
2168 size_t fullSize = rStride * uHeight;
2169 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2170 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2171 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2172 "Invalid size %d for Image, size given in metadata is %d at current stride",
2173 capacity, fullSize);
2174 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002175 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002176
Ruben Brunk47e91f22014-05-28 18:38:42 -07002177 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002178 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002179 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2180 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2181 return;
2182 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002183
Ruben Brunk47e91f22014-05-28 18:38:42 -07002184 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2185 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2186 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2187 sources.add(&stripSource);
2188
2189 status_t ret = OK;
2190 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2191 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002192 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002193 jniThrowExceptionFmt(env, "java/io/IOException",
2194 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002195 }
2196 return;
2197 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002198 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002199 inBuf = new JniInputByteBuffer(env, inBuffer);
2200
2201 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2202 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2203 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2204 sources.add(&stripSource);
2205
2206 status_t ret = OK;
2207 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2208 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2209 if (!env->ExceptionCheck()) {
2210 jniThrowExceptionFmt(env, "java/io/IOException",
2211 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002212 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002213 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002214 }
2215 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002216}
2217
2218static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002219 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002220 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002221
2222 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2223 uint32_t pixStride = BYTES_PER_SAMPLE;
2224 uint32_t uWidth = static_cast<uint32_t>(width);
2225 uint32_t uHeight = static_cast<uint32_t>(height);
2226 uint64_t uOffset = static_cast<uint32_t>(offset);
2227
Dan Albert46d84442014-11-18 16:07:51 -08002228 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2229 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2230 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002231
2232 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002233 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002234 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2235 return;
2236 }
2237
Ruben Brunk47e91f22014-05-28 18:38:42 -07002238 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002239 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002240 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2241 jniThrowException(env, "java/lang/AssertionError",
2242 "Write called with uninitialized DngCreator");
2243 return;
2244 }
Ruben Brunk20796122015-07-21 17:51:54 -07002245 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002246
Ruben Brunk20796122015-07-21 17:51:54 -07002247 if (writer.get() == nullptr) {
2248 return;
2249 }
2250
2251 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002252 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002253 return;
2254 }
2255
2256 sp<DirectStripSource> thumbnailSource;
2257 uint32_t targetIfd = TIFF_IFD_0;
2258 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2259 Vector<StripSource*> sources;
2260
2261 if (hasThumbnail) {
2262 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2263 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2264 uint32_t width = context->getThumbnailWidth();
2265 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2266 width, context->getThumbnailHeight(), bytesPerPixel,
2267 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2268 SAMPLES_PER_RGB_PIXEL);
2269 sources.add(thumbnailSource.get());
2270 targetIfd = TIFF_IFD_SUB1;
2271 }
2272
2273 sp<JniInputStream> in = new JniInputStream(env, inStream);
2274
2275 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2276 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2277 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2278 sources.add(&stripSource);
2279
2280 status_t ret = OK;
2281 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2282 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2283 if (!env->ExceptionCheck()) {
2284 jniThrowExceptionFmt(env, "java/io/IOException",
2285 "Encountered error %d while writing file.", ret);
2286 }
2287 return;
2288 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002289}
2290
2291} /*extern "C" */
2292
2293static JNINativeMethod gDngCreatorMethods[] = {
2294 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2295 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002296 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2297 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002298 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2299 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002300 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2301 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2302 (void*) DngCreator_nativeSetGpsTags},
2303 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2304 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002305 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002306 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002307 (void*) DngCreator_nativeWriteInputStream},
2308};
2309
Ruben Brunkb6079002014-05-22 12:33:54 -07002310int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002311 return RegisterMethodsOrDie(env,
2312 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002313}