blob: 3d021d6cc6b2572b169b9ad36bc5b1c7da84d38c [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -080017//#define LOG_NDEBUG 0
Ruben Brunkf967a542014-04-28 16:31:11 -070018#define LOG_TAG "DngCreator_JNI"
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <string.h>
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070021#include <algorithm>
22#include <memory>
Ruben Brunk9ce22a02015-08-03 12:40:11 -070023#include <vector>
Emilian Peev7ca13712017-04-05 15:42:22 +010024#include <cmath>
Dan Albert46d84442014-11-18 16:07:51 -080025
26#include <utils/Log.h>
27#include <utils/Errors.h>
28#include <utils/StrongPointer.h>
29#include <utils/RefBase.h>
30#include <utils/Vector.h>
Ruben Brunk20796122015-07-21 17:51:54 -070031#include <utils/String8.h>
Dan Albert46d84442014-11-18 16:07:51 -080032#include <cutils/properties.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070033#include <system/camera_metadata.h>
34#include <camera/CameraMetadata.h>
35#include <img_utils/DngUtils.h>
36#include <img_utils/TagDefinitions.h>
37#include <img_utils/TiffIfd.h>
38#include <img_utils/TiffWriter.h>
39#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070040#include <img_utils/Input.h>
41#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070042
Andreas Gampeed6b9df2014-11-20 22:02:20 -080043#include "core_jni_helpers.h"
Ruben Brunkb8df8e02014-06-02 22:59:45 -070044
Ruben Brunkf967a542014-04-28 16:31:11 -070045#include "android_runtime/AndroidRuntime.h"
46#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
47
48#include <jni.h>
49#include <JNIHelp.h>
50
51using namespace android;
52using namespace img_utils;
53
Ruben Brunk20796122015-07-21 17:51:54 -070054#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070055 if ((expr) != OK) { \
56 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070057 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070058 return false; \
Ruben Brunkf967a542014-04-28 16:31:11 -070059 }
60
Ruben Brunk20796122015-07-21 17:51:54 -070061
62#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
63 if ((expr) != OK) { \
64 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
65 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
66 return nullptr; \
67 }
68
69
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070070#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
71 if ((expr) != OK) { \
72 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
73 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
74 return -1; \
75 }
76
Ruben Brunk20796122015-07-21 17:51:54 -070077#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
Chih-Hung Hsieh3c22e002016-05-19 15:10:07 -070078 if ((entry).count == 0) { \
Ruben Brunkf967a542014-04-28 16:31:11 -070079 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070080 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070081 return nullptr; \
Ruben Brunkf967a542014-04-28 16:31:11 -070082 }
83
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -080084#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
85 if (expr) { \
86 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
87 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
88 return nullptr; \
89 }
90
Ruben Brunk20796122015-07-21 17:51:54 -070091
Ruben Brunkb6079002014-05-22 12:33:54 -070092#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070093
94static struct {
95 jfieldID mNativeContext;
96} gDngCreatorClassInfo;
97
98static struct {
99 jmethodID mWriteMethod;
100} gOutputStreamClassInfo;
101
Ruben Brunk47e91f22014-05-28 18:38:42 -0700102static struct {
103 jmethodID mReadMethod;
104 jmethodID mSkipMethod;
105} gInputStreamClassInfo;
106
107static struct {
108 jmethodID mGetMethod;
109} gInputByteBufferClassInfo;
110
Ruben Brunkf967a542014-04-28 16:31:11 -0700111enum {
112 BITS_PER_SAMPLE = 16,
113 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700114 BYTES_PER_RGB_PIXEL = 3,
115 BITS_PER_RGB_SAMPLE = 8,
116 BYTES_PER_RGB_SAMPLE = 1,
117 SAMPLES_PER_RGB_PIXEL = 3,
118 SAMPLES_PER_RAW_PIXEL = 1,
119 TIFF_IFD_0 = 0,
120 TIFF_IFD_SUB1 = 1,
121 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700122};
123
Ruben Brunk20796122015-07-21 17:51:54 -0700124
125/**
126 * POD container class for GPS tag data.
127 */
128class GpsData {
129public:
130 enum {
131 GPS_VALUE_LENGTH = 6,
132 GPS_REF_LENGTH = 2,
133 GPS_DATE_LENGTH = 11,
134 };
135
136 uint32_t mLatitude[GPS_VALUE_LENGTH];
137 uint32_t mLongitude[GPS_VALUE_LENGTH];
138 uint32_t mTimestamp[GPS_VALUE_LENGTH];
139 uint8_t mLatitudeRef[GPS_REF_LENGTH];
140 uint8_t mLongitudeRef[GPS_REF_LENGTH];
141 uint8_t mDate[GPS_DATE_LENGTH];
142};
143
Ruben Brunkf967a542014-04-28 16:31:11 -0700144// ----------------------------------------------------------------------------
145
Ruben Brunk47e91f22014-05-28 18:38:42 -0700146/**
147 * Container class for the persistent native context.
148 */
149
150class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700151public:
Ruben Brunk20796122015-07-21 17:51:54 -0700152 enum {
153 DATETIME_COUNT = 20,
154 };
155
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700156 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700157 virtual ~NativeContext();
158
159 TiffWriter* getWriter();
160
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700161 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
162 std::shared_ptr<const CameraMetadata> getResult() const;
163
Ruben Brunk20796122015-07-21 17:51:54 -0700164 uint32_t getThumbnailWidth() const;
165 uint32_t getThumbnailHeight() const;
166 const uint8_t* getThumbnail() const;
167 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700168
169 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
170
Ruben Brunk20796122015-07-21 17:51:54 -0700171 void setOrientation(uint16_t orientation);
172 uint16_t getOrientation() const;
173
174 void setDescription(const String8& desc);
175 String8 getDescription() const;
176 bool hasDescription() const;
177
178 void setGpsData(const GpsData& data);
179 GpsData getGpsData() const;
180 bool hasGpsData() const;
181
182 void setCaptureTime(const String8& formattedCaptureTime);
183 String8 getCaptureTime() const;
184 bool hasCaptureTime() const;
185
Ruben Brunk47e91f22014-05-28 18:38:42 -0700186private:
187 Vector<uint8_t> mCurrentThumbnail;
188 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700189 std::shared_ptr<CameraMetadata> mCharacteristics;
190 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700191 uint32_t mThumbnailWidth;
192 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700193 uint16_t mOrientation;
194 bool mThumbnailSet;
195 bool mGpsSet;
196 bool mDescriptionSet;
197 bool mCaptureTimeSet;
198 String8 mDescription;
199 GpsData mGpsData;
200 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700201};
202
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700203NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
204 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
205 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -0800206 mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
207 mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700208
209NativeContext::~NativeContext() {}
210
211TiffWriter* NativeContext::getWriter() {
212 return &mWriter;
213}
214
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700215std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
216 return mCharacteristics;
217}
218
219std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
220 return mResult;
221}
222
Ruben Brunk20796122015-07-21 17:51:54 -0700223uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700224 return mThumbnailWidth;
225}
226
Ruben Brunk20796122015-07-21 17:51:54 -0700227uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700228 return mThumbnailHeight;
229}
230
Ruben Brunk20796122015-07-21 17:51:54 -0700231const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700232 return mCurrentThumbnail.array();
233}
234
Ruben Brunk20796122015-07-21 17:51:54 -0700235bool NativeContext::hasThumbnail() const {
236 return mThumbnailSet;
237}
238
Ruben Brunk47e91f22014-05-28 18:38:42 -0700239bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
240 mThumbnailWidth = width;
241 mThumbnailHeight = height;
242
243 size_t size = BYTES_PER_RGB_PIXEL * width * height;
244 if (mCurrentThumbnail.resize(size) < 0) {
245 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
246 return false;
247 }
248
249 uint8_t* thumb = mCurrentThumbnail.editArray();
250 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700251 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700252 return true;
253}
254
Ruben Brunk20796122015-07-21 17:51:54 -0700255void NativeContext::setOrientation(uint16_t orientation) {
256 mOrientation = orientation;
257}
258
259uint16_t NativeContext::getOrientation() const {
260 return mOrientation;
261}
262
263void NativeContext::setDescription(const String8& desc) {
264 mDescription = desc;
265 mDescriptionSet = true;
266}
267
268String8 NativeContext::getDescription() const {
269 return mDescription;
270}
271
272bool NativeContext::hasDescription() const {
273 return mDescriptionSet;
274}
275
276void NativeContext::setGpsData(const GpsData& data) {
277 mGpsData = data;
278 mGpsSet = true;
279}
280
281GpsData NativeContext::getGpsData() const {
282 return mGpsData;
283}
284
285bool NativeContext::hasGpsData() const {
286 return mGpsSet;
287}
288
289void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
290 mFormattedCaptureTime = formattedCaptureTime;
291 mCaptureTimeSet = true;
292}
293
294String8 NativeContext::getCaptureTime() const {
295 return mFormattedCaptureTime;
296}
297
298bool NativeContext::hasCaptureTime() const {
299 return mCaptureTimeSet;
300}
301
Ruben Brunk47e91f22014-05-28 18:38:42 -0700302// End of NativeContext
303// ----------------------------------------------------------------------------
304
305/**
306 * Wrapper class for a Java OutputStream.
307 *
308 * This class is not intended to be used across JNI calls.
309 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700310class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
311public:
312 JniOutputStream(JNIEnv* env, jobject outStream);
313
314 virtual ~JniOutputStream();
315
316 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700317
Ruben Brunkf967a542014-04-28 16:31:11 -0700318 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700319
Ruben Brunkf967a542014-04-28 16:31:11 -0700320 status_t close();
321private:
322 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700323 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700324 };
325 jobject mOutputStream;
326 JNIEnv* mEnv;
327 jbyteArray mByteArray;
328};
329
330JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
331 mEnv(env) {
332 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700333 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700334 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
335 }
336}
337
338JniOutputStream::~JniOutputStream() {
339 mEnv->DeleteLocalRef(mByteArray);
340}
341
342status_t JniOutputStream::open() {
343 // Do nothing
344 return OK;
345}
346
347status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
348 while(count > 0) {
349 size_t len = BYTE_ARRAY_LENGTH;
350 len = (count > len) ? len : count;
351 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
352
353 if (mEnv->ExceptionCheck()) {
354 return BAD_VALUE;
355 }
356
357 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
358 0, len);
359
360 if (mEnv->ExceptionCheck()) {
361 return BAD_VALUE;
362 }
363
364 count -= len;
365 offset += len;
366 }
367 return OK;
368}
369
370status_t JniOutputStream::close() {
371 // Do nothing
372 return OK;
373}
374
Ruben Brunk47e91f22014-05-28 18:38:42 -0700375// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700376// ----------------------------------------------------------------------------
377
Ruben Brunk47e91f22014-05-28 18:38:42 -0700378/**
379 * Wrapper class for a Java InputStream.
380 *
381 * This class is not intended to be used across JNI calls.
382 */
383class JniInputStream : public Input, public LightRefBase<JniInputStream> {
384public:
385 JniInputStream(JNIEnv* env, jobject inStream);
386
387 status_t open();
388
389 status_t close();
390
391 ssize_t read(uint8_t* buf, size_t offset, size_t count);
392
393 ssize_t skip(size_t count);
394
395 virtual ~JniInputStream();
396private:
397 enum {
398 BYTE_ARRAY_LENGTH = 4096
399 };
400 jobject mInStream;
401 JNIEnv* mEnv;
402 jbyteArray mByteArray;
403
404};
405
406JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
407 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700408 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700409 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
410 }
411}
412
413JniInputStream::~JniInputStream() {
414 mEnv->DeleteLocalRef(mByteArray);
415}
416
417ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
418
419 jint realCount = BYTE_ARRAY_LENGTH;
420 if (count < BYTE_ARRAY_LENGTH) {
421 realCount = count;
422 }
423 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
424 realCount);
425
426 if (actual < 0) {
427 return NOT_ENOUGH_DATA;
428 }
429
430 if (mEnv->ExceptionCheck()) {
431 return BAD_VALUE;
432 }
433
434 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
435 if (mEnv->ExceptionCheck()) {
436 return BAD_VALUE;
437 }
438 return actual;
439}
440
441ssize_t JniInputStream::skip(size_t count) {
442 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
443 static_cast<jlong>(count));
444
445 if (mEnv->ExceptionCheck()) {
446 return BAD_VALUE;
447 }
448 if (actual < 0) {
449 return NOT_ENOUGH_DATA;
450 }
451 return actual;
452}
453
454status_t JniInputStream::open() {
455 // Do nothing
456 return OK;
457}
458
459status_t JniInputStream::close() {
460 // Do nothing
461 return OK;
462}
463
464// End of JniInputStream
465// ----------------------------------------------------------------------------
466
467/**
468 * Wrapper class for a non-direct Java ByteBuffer.
469 *
470 * This class is not intended to be used across JNI calls.
471 */
472class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
473public:
474 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
475
476 status_t open();
477
478 status_t close();
479
480 ssize_t read(uint8_t* buf, size_t offset, size_t count);
481
482 virtual ~JniInputByteBuffer();
483private:
484 enum {
485 BYTE_ARRAY_LENGTH = 4096
486 };
487 jobject mInBuf;
488 JNIEnv* mEnv;
489 jbyteArray mByteArray;
490};
491
492JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
493 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700494 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700495 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
496 }
497}
498
499JniInputByteBuffer::~JniInputByteBuffer() {
500 mEnv->DeleteLocalRef(mByteArray);
501}
502
503ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
504 jint realCount = BYTE_ARRAY_LENGTH;
505 if (count < BYTE_ARRAY_LENGTH) {
506 realCount = count;
507 }
508
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700509 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
510 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800511 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700512
513 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800514 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700515 return BAD_VALUE;
516 }
517
518 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
519 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800520 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700521 return BAD_VALUE;
522 }
523 return realCount;
524}
525
526status_t JniInputByteBuffer::open() {
527 // Do nothing
528 return OK;
529}
530
531status_t JniInputByteBuffer::close() {
532 // Do nothing
533 return OK;
534}
535
536// End of JniInputByteBuffer
537// ----------------------------------------------------------------------------
538
539/**
540 * StripSource subclass for Input types.
541 *
542 * This class is not intended to be used across JNI calls.
543 */
544
545class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
546public:
547 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
548 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
549 uint32_t samplesPerPixel);
550
551 virtual ~InputStripSource();
552
553 virtual status_t writeToStream(Output& stream, uint32_t count);
554
555 virtual uint32_t getIfd() const;
556protected:
557 uint32_t mIfd;
558 Input* mInput;
559 uint32_t mWidth;
560 uint32_t mHeight;
561 uint32_t mPixStride;
562 uint32_t mRowStride;
563 uint64_t mOffset;
564 JNIEnv* mEnv;
565 uint32_t mBytesPerSample;
566 uint32_t mSamplesPerPixel;
567};
568
569InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
570 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
571 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
572 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
573 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
574 mSamplesPerPixel(samplesPerPixel) {}
575
576InputStripSource::~InputStripSource() {}
577
578status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700579 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700580 jlong offset = mOffset;
581
582 if (fullSize != count) {
583 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
584 fullSize);
585 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
586 return BAD_VALUE;
587 }
588
589 // Skip offset
590 while (offset > 0) {
591 ssize_t skipped = mInput->skip(offset);
592 if (skipped <= 0) {
593 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
594 jniThrowExceptionFmt(mEnv, "java/io/IOException",
595 "Early EOF encountered in skip, not enough pixel data for image of size %u",
596 fullSize);
597 skipped = NOT_ENOUGH_DATA;
598 } else {
599 if (!mEnv->ExceptionCheck()) {
600 jniThrowException(mEnv, "java/io/IOException",
601 "Error encountered while skip bytes in input stream.");
602 }
603 }
604
605 return skipped;
606 }
607 offset -= skipped;
608 }
609
610 Vector<uint8_t> row;
611 if (row.resize(mRowStride) < 0) {
612 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
613 return BAD_VALUE;
614 }
615
616 uint8_t* rowBytes = row.editArray();
617
618 for (uint32_t i = 0; i < mHeight; ++i) {
619 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800620 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700621
622 while (rowFillAmt < mRowStride) {
623 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
624 if (bytesRead <= 0) {
625 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800626 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
627 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700628 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800629 "Early EOF encountered, not enough pixel data for image of size %"
630 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700631 bytesRead = NOT_ENOUGH_DATA;
632 } else {
633 if (!mEnv->ExceptionCheck()) {
634 jniThrowException(mEnv, "java/io/IOException",
635 "Error encountered while reading");
636 }
637 }
638 return bytesRead;
639 }
640 rowFillAmt += bytesRead;
641 rowSize -= bytesRead;
642 }
643
644 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
645 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
646
647 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
648 mEnv->ExceptionCheck()) {
649 if (!mEnv->ExceptionCheck()) {
650 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
651 }
652 return BAD_VALUE;
653 }
654 } else {
655 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
656 jniThrowException(mEnv, "java/lang/IllegalStateException",
657 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
658 return BAD_VALUE;
659
660 // TODO: Add support for non-contiguous pixels if needed.
661 }
662 }
663 return OK;
664}
665
666uint32_t InputStripSource::getIfd() const {
667 return mIfd;
668}
669
670// End of InputStripSource
671// ----------------------------------------------------------------------------
672
673/**
674 * StripSource subclass for direct buffer types.
675 *
676 * This class is not intended to be used across JNI calls.
677 */
678
679class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
680public:
681 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
682 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
683 uint32_t bytesPerSample, uint32_t samplesPerPixel);
684
685 virtual ~DirectStripSource();
686
687 virtual status_t writeToStream(Output& stream, uint32_t count);
688
689 virtual uint32_t getIfd() const;
690protected:
691 uint32_t mIfd;
692 const uint8_t* mPixelBytes;
693 uint32_t mWidth;
694 uint32_t mHeight;
695 uint32_t mPixStride;
696 uint32_t mRowStride;
697 uint16_t mOffset;
698 JNIEnv* mEnv;
699 uint32_t mBytesPerSample;
700 uint32_t mSamplesPerPixel;
701};
702
703DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
704 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
705 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
706 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
707 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
708 mSamplesPerPixel(samplesPerPixel) {}
709
710DirectStripSource::~DirectStripSource() {}
711
712status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700713 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700714
715 if (fullSize != count) {
716 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
717 fullSize);
718 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
719 return BAD_VALUE;
720 }
721
Ruben Brunk20796122015-07-21 17:51:54 -0700722
Ruben Brunk47e91f22014-05-28 18:38:42 -0700723 if (mPixStride == mBytesPerSample * mSamplesPerPixel
724 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
725 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
726
727 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
728 if (!mEnv->ExceptionCheck()) {
729 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
730 }
731 return BAD_VALUE;
732 }
733 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
734 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
735
736 for (size_t i = 0; i < mHeight; ++i) {
737 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
738 mEnv->ExceptionCheck()) {
739 if (!mEnv->ExceptionCheck()) {
740 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
741 }
742 return BAD_VALUE;
743 }
744 }
745 } else {
746 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
747
748 jniThrowException(mEnv, "java/lang/IllegalStateException",
749 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
750 return BAD_VALUE;
751
752 // TODO: Add support for non-contiguous pixels if needed.
753 }
754 return OK;
755
756}
757
758uint32_t DirectStripSource::getIfd() const {
759 return mIfd;
760}
761
762// End of DirectStripSource
763// ----------------------------------------------------------------------------
764
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700765/**
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700766 * Calculate the default crop relative to the "active area" of the image sensor (this active area
767 * will always be the pre-correction active area rectangle), and set this.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700768 */
769static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700770 sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700771
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700772 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700773 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700774 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
775 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
776
Ruben Brunk20796122015-07-21 17:51:54 -0700777 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
778
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700779 if (width < margin * 2 || height < margin * 2) {
780 ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too"
781 "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width);
782 jniThrowException(env, "java/lang/IllegalStateException",
783 "Pre-correction active area is too small.");
784 return BAD_VALUE;
Ruben Brunk20796122015-07-21 17:51:54 -0700785 }
786
Ruben Brunka4ff47c2015-08-27 14:00:03 -0700787 uint32_t defaultCropOrigin[] = {margin, margin};
788 uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin,
789 height - defaultCropOrigin[1] - margin};
790
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700791 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
792 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
793 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
794 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
795
796 return OK;
797}
798
Ruben Brunk20796122015-07-21 17:51:54 -0700799static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700800 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700801 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700802 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700803 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700804 return false;
805 }
806
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700807 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700808 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700809 "Image height %d is invalid", height);
810 return false;
811 }
812
813 camera_metadata_ro_entry preCorrectionEntry =
814 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
815 camera_metadata_ro_entry pixelArrayEntry =
816 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
817
818 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
819 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
820 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
821 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
822
823 bool matchesPixelArray = (pWidth == width && pHeight == height);
824 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
825
Ruben Brunk20796122015-07-21 17:51:54 -0700826 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700827 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
828 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
829 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
830 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700831 return false;
832 }
833
834 return true;
835}
836
Ruben Brunk20796122015-07-21 17:51:54 -0700837static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700838 const Vector<uint16_t>& entries) {
839 for (size_t i = 0; i < entries.size(); ++i) {
840 uint16_t tagId = entries[i];
841 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700842 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700843 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
844 ifdFrom);
845 return BAD_VALUE;
846 }
847 if (writer->addEntry(entry, ifdTo) != OK) {
848 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
849 ifdFrom);
850 return BAD_VALUE;
851 }
852 writer->removeEntry(tagId, ifdFrom);
853 }
854 return OK;
855}
856
Ruben Brunkd70132c2014-08-22 16:24:49 -0700857/**
858 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
859 * Returns OK on success, or a negative error code if the CFA enum was invalid.
860 */
861static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
862 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
863 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
864 cfaEnum);
865 switch(cfa) {
866 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
867 cfaOut[0] = 0;
868 cfaOut[1] = 1;
869 cfaOut[2] = 1;
870 cfaOut[3] = 2;
871 break;
872 }
873 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
874 cfaOut[0] = 1;
875 cfaOut[1] = 0;
876 cfaOut[2] = 2;
877 cfaOut[3] = 1;
878 break;
879 }
880 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
881 cfaOut[0] = 1;
882 cfaOut[1] = 2;
883 cfaOut[2] = 0;
884 cfaOut[3] = 1;
885 break;
886 }
887 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
888 cfaOut[0] = 2;
889 cfaOut[1] = 1;
890 cfaOut[2] = 1;
891 cfaOut[3] = 0;
892 break;
893 }
894 default: {
895 return BAD_VALUE;
896 }
897 }
898 return OK;
899}
900
901/**
902 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
903 * RGGB for an unknown enum.
904 */
905static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
906 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
907 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
908 cfaEnum);
909 switch(cfa) {
910 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
911 return OpcodeListBuilder::CFA_RGGB;
912 }
913 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
914 return OpcodeListBuilder::CFA_GRBG;
915 }
916 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
917 return OpcodeListBuilder::CFA_GBRG;
918 }
919 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
920 return OpcodeListBuilder::CFA_BGGR;
921 }
922 default: {
923 return OpcodeListBuilder::CFA_RGGB;
924 }
925 }
926}
927
928/**
929 * For each color plane, find the corresponding noise profile coefficients given in the
930 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
931 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
932 *
933 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
934 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
935 * coefficients.
936 * numChannels - the number of noise profile coefficient pairs and color channels given in
937 * the perChannelNoiseProfile and cfa arguments, respectively.
938 * planeColors - the color planes in the noise profile output.
939 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
940 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
941 *
942 * returns OK, or a negative error code on failure.
943 */
944static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
945 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
946 /*out*/double* noiseProfile) {
947
948 for (size_t p = 0; p < numPlanes; ++p) {
949 size_t S = p * 2;
950 size_t O = p * 2 + 1;
951
952 noiseProfile[S] = 0;
953 noiseProfile[O] = 0;
954 bool uninitialized = true;
955 for (size_t c = 0; c < numChannels; ++c) {
956 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
957 noiseProfile[S] = perChannelNoiseProfile[c * 2];
958 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
959 uninitialized = false;
960 }
961 }
962 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800963 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
964 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700965 return BAD_VALUE;
966 }
967 }
968 return OK;
969}
970
Ruben Brunk47e91f22014-05-28 18:38:42 -0700971// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700972extern "C" {
973
Ruben Brunk47e91f22014-05-28 18:38:42 -0700974static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700975 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700976 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700977 gDngCreatorClassInfo.mNativeContext));
978}
979
Ruben Brunk47e91f22014-05-28 18:38:42 -0700980static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700981 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700982 NativeContext* current = DngCreator_getNativeContext(env, thiz);
983
Ruben Brunk20796122015-07-21 17:51:54 -0700984 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700985 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700986 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700987
Ruben Brunkf967a542014-04-28 16:31:11 -0700988 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700989 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700990 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700991
Ruben Brunkf967a542014-04-28 16:31:11 -0700992 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700993 reinterpret_cast<jlong>(context.get()));
994}
995
Ruben Brunkf967a542014-04-28 16:31:11 -0700996static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
997 ALOGV("%s:", __FUNCTION__);
998
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800999 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1000 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001001
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001002 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1003 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1004 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001005
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001006 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1007 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1008 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001009
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001010 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1011 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1012 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001013}
1014
1015static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001016 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001017 ALOGV("%s:", __FUNCTION__);
1018 CameraMetadata characteristics;
1019 CameraMetadata results;
1020 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1021 jniThrowException(env, "java/lang/AssertionError",
1022 "No native metadata defined for camera characteristics.");
1023 return;
1024 }
1025 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1026 jniThrowException(env, "java/lang/AssertionError",
1027 "No native metadata defined for capture results.");
1028 return;
1029 }
1030
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001031 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001032
1033 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1034
1035 size_t len = strlen(captureTime) + 1;
1036 if (len != NativeContext::DATETIME_COUNT) {
1037 jniThrowException(env, "java/lang/IllegalArgumentException",
1038 "Formatted capture time string length is not required 20 characters");
1039 return;
1040 }
1041
1042 nativeContext->setCaptureTime(String8(captureTime));
1043
1044 DngCreator_setNativeContext(env, thiz, nativeContext);
1045}
1046
1047static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1048 uint32_t imageHeight) {
1049
1050 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1051
1052 if (nativeContext == nullptr) {
1053 jniThrowException(env, "java/lang/AssertionError",
1054 "No native context, must call init before other operations.");
1055 return nullptr;
1056 }
1057
1058 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1059 CameraMetadata results = *(nativeContext->getResult());
1060
1061 sp<TiffWriter> writer = new TiffWriter();
1062
1063 uint32_t preWidth = 0;
1064 uint32_t preHeight = 0;
1065 {
1066 // Check dimensions
1067 camera_metadata_entry entry =
1068 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1069 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1070 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1071 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1072
1073 camera_metadata_entry pixelArrayEntry =
1074 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1075 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1076 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1077
1078 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1079 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1080 jniThrowException(env, "java/lang/AssertionError",
1081 "Height and width of imate buffer did not match height and width of"
1082 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1083 return nullptr;
1084 }
1085 }
1086
1087
Ruben Brunkf967a542014-04-28 16:31:11 -07001088
1089 writer->addIfd(TIFF_IFD_0);
1090
1091 status_t err = OK;
1092
1093 const uint32_t samplesPerPixel = 1;
1094 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001095
1096 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001097 uint8_t cfaPlaneColor[3] = {0, 1, 2};
1098 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001099
1100 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001101 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001102
1103 // Setup main image tags
1104
Ruben Brunkf967a542014-04-28 16:31:11 -07001105 {
1106 // Set orientation
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001107 uint16_t orientation = TAG_ORIENTATION_NORMAL;
Ruben Brunk20796122015-07-21 17:51:54 -07001108 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1109 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001110 }
1111
1112 {
1113 // Set subfiletype
1114 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001115 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1116 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001117 }
1118
1119 {
1120 // Set bits per sample
1121 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001122 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001123 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001124 }
1125
1126 {
1127 // Set compression
1128 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001129 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1130 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001131 }
1132
1133 {
1134 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001135 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1136 env, TAG_IMAGEWIDTH, writer);
1137 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1138 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001139 }
1140
1141 {
1142 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -07001143 uint16_t interpretation = 32803; // CFA
Ruben Brunk20796122015-07-21 17:51:54 -07001144 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1145 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001146 }
1147
1148 {
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001149 // Set blacklevel tags, using dynamic black level if available
Ruben Brunkf967a542014-04-28 16:31:11 -07001150 camera_metadata_entry entry =
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001151 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
1152 uint32_t blackLevelRational[8] = {0};
1153 if (entry.count != 0) {
1154 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1155 for (size_t i = 0; i < entry.count; i++) {
1156 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
1157 blackLevelRational[i * 2 + 1] = 100;
1158 }
1159 } else {
1160 // Fall back to static black level which is guaranteed
1161 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
1162 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
1163 for (size_t i = 0; i < entry.count; i++) {
1164 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
1165 blackLevelRational[i * 2 + 1] = 1;
1166 }
1167
1168 }
1169 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
Ruben Brunk20796122015-07-21 17:51:54 -07001170 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001171
1172 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001173 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1174 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001175 }
1176
1177 {
1178 // Set samples per pixel
1179 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001180 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001181 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001182 }
1183
1184 {
1185 // Set planar configuration
1186 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001187 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1188 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001189 }
1190
1191 {
1192 // Set CFA pattern dimensions
1193 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001194 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1195 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001196 }
1197
1198 {
1199 // Set CFA pattern
1200 camera_metadata_entry entry =
1201 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk20796122015-07-21 17:51:54 -07001202 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001203
1204 const int cfaLength = 4;
1205 cfaEnum = entry.data.u8[0];
1206 uint8_t cfa[cfaLength];
1207 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1208 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1209 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001210 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001211
Ruben Brunk20796122015-07-21 17:51:54 -07001212 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1213 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001214
1215 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001216 }
1217
1218 {
1219 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001220 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1221 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001222 }
1223
1224 {
1225 // Set CFA layout
1226 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001227 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001228 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001229 }
1230
1231 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001232 // image description
1233 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001234 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1235 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001236 }
1237
1238 {
1239 // make
1240 char manufacturer[PROPERTY_VALUE_MAX];
1241
1242 // Use "" to represent unknown make as suggested in TIFF/EP spec.
1243 property_get("ro.product.manufacturer", manufacturer, "");
1244 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1245
Ruben Brunk20796122015-07-21 17:51:54 -07001246 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
1247 reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001248 }
1249
1250 {
1251 // model
1252 char model[PROPERTY_VALUE_MAX];
1253
1254 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1255 property_get("ro.product.model", model, "");
1256 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1257
Ruben Brunk20796122015-07-21 17:51:54 -07001258 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
1259 reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001260 }
1261
1262 {
1263 // x resolution
1264 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001265 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001266 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001267
1268 // y resolution
1269 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001270 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001271 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001272
1273 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001274 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001275 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001276 }
1277
1278 {
1279 // software
1280 char software[PROPERTY_VALUE_MAX];
1281 property_get("ro.build.fingerprint", software, "");
1282 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001283 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
1284 reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001285 }
1286
Ruben Brunk20796122015-07-21 17:51:54 -07001287 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001288 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001289 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001290
Ruben Brunk20796122015-07-21 17:51:54 -07001291 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1292 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001293 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1294 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001295 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001296 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001297
1298 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001299 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1300 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001301 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1302 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001303 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001304 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001305 }
1306
1307 {
1308 // TIFF/EP standard id
1309 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001310 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001311 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001312 }
1313
1314 {
1315 // copyright
1316 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001317 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001318 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001319 }
1320
1321 {
1322 // exposure time
1323 camera_metadata_entry entry =
1324 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001325 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001326
1327 int64_t exposureTime = *(entry.data.i64);
1328
1329 if (exposureTime < 0) {
1330 // Should be unreachable
1331 jniThrowException(env, "java/lang/IllegalArgumentException",
1332 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001333 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001334 }
1335
1336 // Ensure exposure time doesn't overflow (for exposures > 4s)
1337 uint32_t denominator = 1000000000;
1338 while (exposureTime > UINT32_MAX) {
1339 exposureTime >>= 1;
1340 denominator >>= 1;
1341 if (denominator == 0) {
1342 // Should be unreachable
1343 jniThrowException(env, "java/lang/IllegalArgumentException",
1344 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001345 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001346 }
1347 }
1348
1349 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001350 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001351 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001352
1353 }
1354
1355 {
1356 // ISO speed ratings
1357 camera_metadata_entry entry =
1358 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001359 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001360
1361 int32_t tempIso = *(entry.data.i32);
1362 if (tempIso < 0) {
1363 jniThrowException(env, "java/lang/IllegalArgumentException",
1364 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001365 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001366 }
1367
1368 if (tempIso > UINT16_MAX) {
1369 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1370 tempIso = UINT16_MAX;
1371 }
1372
1373 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001374 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001375 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001376 }
1377
1378 {
Emilian Peev7ca13712017-04-05 15:42:22 +01001379 // Baseline exposure
1380 camera_metadata_entry entry =
1381 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
1382 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer);
1383
1384 // post RAW gain should be boostValue / 100
1385 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f;
1386 // Baseline exposure should be in EV units so log2(gain) =
1387 // log10(gain)/log10(2)
1388 double baselineExposure = std::log(postRAWGain) / std::log(2.0f);
1389 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100),
1390 100 };
1391 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1,
1392 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer);
1393 }
1394
1395 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001396 // focal length
1397 camera_metadata_entry entry =
1398 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001399 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001400
1401 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001402 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001403 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001404 }
1405
1406 {
1407 // f number
1408 camera_metadata_entry entry =
1409 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001410 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001411
1412 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001413 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001414 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001415 }
1416
1417 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001418 // Set DNG version information
1419 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001420 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001421 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001422
1423 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001424 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1425 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001426 }
1427
1428 {
1429 // Set whitelevel
1430 camera_metadata_entry entry =
1431 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001432 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001433 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001434 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1435 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001436 }
1437
1438 {
1439 // Set default scale
1440 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001441 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1442 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001443 }
1444
1445 bool singleIlluminant = false;
1446 {
1447 // Set calibration illuminants
1448 camera_metadata_entry entry1 =
1449 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001450 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001451 camera_metadata_entry entry2 =
1452 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1453 if (entry2.count == 0) {
1454 singleIlluminant = true;
1455 }
1456 uint16_t ref1 = entry1.data.u8[0];
1457
Ruben Brunk20796122015-07-21 17:51:54 -07001458 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001459 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001460
1461 if (!singleIlluminant) {
1462 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001463 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001464 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001465 }
1466 }
1467
1468 {
1469 // Set color transforms
1470 camera_metadata_entry entry1 =
1471 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001472 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001473
1474 int32_t colorTransform1[entry1.count * 2];
1475
1476 size_t ctr = 0;
1477 for(size_t i = 0; i < entry1.count; ++i) {
1478 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1479 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1480 }
1481
Ruben Brunk20796122015-07-21 17:51:54 -07001482 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1483 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001484
1485 if (!singleIlluminant) {
1486 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001487 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001488 int32_t colorTransform2[entry2.count * 2];
1489
1490 ctr = 0;
1491 for(size_t i = 0; i < entry2.count; ++i) {
1492 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1493 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1494 }
1495
Ruben Brunk20796122015-07-21 17:51:54 -07001496 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1497 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001498 }
1499 }
1500
1501 {
1502 // Set calibration transforms
1503 camera_metadata_entry entry1 =
1504 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001505 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001506
1507 int32_t calibrationTransform1[entry1.count * 2];
1508
1509 size_t ctr = 0;
1510 for(size_t i = 0; i < entry1.count; ++i) {
1511 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1512 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1513 }
1514
Ruben Brunk20796122015-07-21 17:51:54 -07001515 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001516 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001517
1518 if (!singleIlluminant) {
1519 camera_metadata_entry entry2 =
1520 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001521 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001522 int32_t calibrationTransform2[entry2.count * 2];
1523
1524 ctr = 0;
1525 for(size_t i = 0; i < entry2.count; ++i) {
1526 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1527 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1528 }
1529
Ruben Brunk20796122015-07-21 17:51:54 -07001530 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001531 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001532 }
1533 }
1534
1535 {
1536 // Set forward transforms
1537 camera_metadata_entry entry1 =
1538 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001539 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001540
1541 int32_t forwardTransform1[entry1.count * 2];
1542
1543 size_t ctr = 0;
1544 for(size_t i = 0; i < entry1.count; ++i) {
1545 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1546 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1547 }
1548
Ruben Brunk20796122015-07-21 17:51:54 -07001549 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1550 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001551
1552 if (!singleIlluminant) {
1553 camera_metadata_entry entry2 =
1554 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001555 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001556 int32_t forwardTransform2[entry2.count * 2];
1557
1558 ctr = 0;
1559 for(size_t i = 0; i < entry2.count; ++i) {
1560 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1561 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1562 }
1563
Ruben Brunk20796122015-07-21 17:51:54 -07001564 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1565 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001566 }
1567 }
1568
1569 {
1570 // Set camera neutral
1571 camera_metadata_entry entry =
1572 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001573 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001574 uint32_t cameraNeutral[entry.count * 2];
1575
1576 size_t ctr = 0;
1577 for(size_t i = 0; i < entry.count; ++i) {
1578 cameraNeutral[ctr++] =
1579 static_cast<uint32_t>(entry.data.r[i].numerator);
1580 cameraNeutral[ctr++] =
1581 static_cast<uint32_t>(entry.data.r[i].denominator);
1582 }
1583
Ruben Brunk20796122015-07-21 17:51:54 -07001584 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001585 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001586 }
1587
Ruben Brunkf967a542014-04-28 16:31:11 -07001588
1589 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001590 // Set dimensions
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001591 if (calculateAndSetCrop(env, characteristics, writer) != OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001592 return nullptr;
1593 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001594 camera_metadata_entry entry =
1595 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001596 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001597 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1598 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1599 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1600 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001601
Ruben Brunka4ff47c2015-08-27 14:00:03 -07001602 // If we only have a buffer containing the pre-correction rectangle, ignore the offset
1603 // relative to the pixel array.
1604 if (imageWidth == width && imageHeight == height) {
1605 xmin = 0;
1606 ymin = 0;
1607 }
1608
Ruben Brunk20796122015-07-21 17:51:54 -07001609 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1610 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1611 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001612 }
1613
1614 {
1615 // Setup unique camera model tag
1616 char model[PROPERTY_VALUE_MAX];
1617 property_get("ro.product.model", model, "");
1618
1619 char manufacturer[PROPERTY_VALUE_MAX];
1620 property_get("ro.product.manufacturer", manufacturer, "");
1621
1622 char brand[PROPERTY_VALUE_MAX];
1623 property_get("ro.product.brand", brand, "");
1624
1625 String8 cameraModel(model);
1626 cameraModel += "-";
1627 cameraModel += manufacturer;
1628 cameraModel += "-";
1629 cameraModel += brand;
1630
Ruben Brunk20796122015-07-21 17:51:54 -07001631 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001632 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001633 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001634 }
1635
1636 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001637 // Setup sensor noise model
1638 camera_metadata_entry entry =
1639 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1640
Ruben Brunkd70132c2014-08-22 16:24:49 -07001641 const status_t numPlaneColors = 3;
1642 const status_t numCfaChannels = 4;
1643
1644 uint8_t cfaOut[numCfaChannels];
1645 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1646 jniThrowException(env, "java/lang/IllegalArgumentException",
1647 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001648 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001649 }
1650
1651 double noiseProfile[numPlaneColors * 2];
1652
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001653 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001654 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001655 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1656 "in characteristics, no noise profile tag written...",
1657 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001658 } else {
1659 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1660 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1661
Ruben Brunk20796122015-07-21 17:51:54 -07001662 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1663 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1664 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001665 } else {
1666 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1667 " tag written...", __FUNCTION__);
1668 }
1669 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001670 } else {
1671 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1672 __FUNCTION__);
1673 }
1674 }
1675
1676 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001677 // Set up opcode List 2
1678 OpcodeListBuilder builder;
1679 status_t err = OK;
1680
1681 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001682 camera_metadata_entry entry1 =
1683 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001684
1685 uint32_t lsmWidth = 0;
1686 uint32_t lsmHeight = 0;
1687
1688 if (entry1.count != 0) {
1689 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1690 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1691 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001692
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001693 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001694
Ruben Brunk20796122015-07-21 17:51:54 -07001695 camera_metadata_entry entry =
1696 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1697 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1698 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1699 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1700 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1701 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001702 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkc03443b2015-10-14 17:46:52 -07001703 // GainMap rectangle is relative to the active area origin.
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001704 err = builder.addGainMapsForMetadata(lsmWidth,
1705 lsmHeight,
Ruben Brunkc03443b2015-10-14 17:46:52 -07001706 0,
1707 0,
Ruben Brunk20796122015-07-21 17:51:54 -07001708 height,
1709 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001710 opcodeCfaLayout,
1711 entry2.data.f);
1712 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001713 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1714 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001715 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001716 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001717 }
1718
Ruben Brunk9ce22a02015-08-03 12:40:11 -07001719
1720 // Set up bad pixel correction list
1721 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
1722
1723 if ((entry3.count % 2) != 0) {
1724 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
1725 __FUNCTION__);
1726 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1727 return nullptr;
1728 }
1729
1730 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
1731 std::vector<uint32_t> v;
1732 for (size_t i = 0; i < entry3.count; i+=2) {
1733 int32_t x = entry3.data.i32[i];
1734 int32_t y = entry3.data.i32[i + 1];
1735 x -= static_cast<int32_t>(xmin);
1736 y -= static_cast<int32_t>(ymin);
1737 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
1738 static_cast<uint32_t>(y) >= width) {
1739 continue;
1740 }
1741 v.push_back(x);
1742 v.push_back(y);
1743 }
1744 const uint32_t* badPixels = &v[0];
1745 uint32_t badPixelCount = v.size();
1746
1747 if (badPixelCount > 0) {
1748 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
1749
1750 if (err != OK) {
1751 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
1752 jniThrowRuntimeException(env, "failed to add hotpixel map.");
1753 return nullptr;
1754 }
1755 }
1756
1757
Ruben Brunkfe816622015-06-16 23:03:09 -07001758 size_t listSize = builder.getSize();
1759 uint8_t opcodeListBuf[listSize];
1760 err = builder.buildOpList(opcodeListBuf);
1761 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001762 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001763 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1764 } else {
1765 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1766 "map.", __FUNCTION__);
1767 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1768 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001769 return nullptr;
Ruben Brunkfe816622015-06-16 23:03:09 -07001770 }
1771 }
1772
1773 {
1774 // Set up opcode List 3
1775 OpcodeListBuilder builder;
1776 status_t err = OK;
1777
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001778 // Set up rectilinear distortion correction
1779 camera_metadata_entry entry3 =
1780 results.find(ANDROID_LENS_RADIAL_DISTORTION);
1781 camera_metadata_entry entry4 =
1782 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1783
1784 if (entry3.count == 6 && entry4.count == 5) {
1785 float cx = entry4.data.f[/*c_x*/2];
1786 float cy = entry4.data.f[/*c_y*/3];
Ruben Brunk20796122015-07-21 17:51:54 -07001787 err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001788 cy);
1789 if (err != OK) {
1790 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1791 jniThrowRuntimeException(env, "failed to add distortion correction.");
Ruben Brunk20796122015-07-21 17:51:54 -07001792 return nullptr;
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001793 }
1794 }
1795
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001796 size_t listSize = builder.getSize();
1797 uint8_t opcodeListBuf[listSize];
1798 err = builder.buildOpList(opcodeListBuf);
1799 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001800 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001801 TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001802 } else {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001803 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1804 "map.", __FUNCTION__);
1805 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1806 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001807 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001808 }
1809 }
1810
Ruben Brunk20796122015-07-21 17:51:54 -07001811 {
1812 // Set up orientation tags.
Eino-Ville Talvala8c35d5b2016-03-08 15:44:24 -08001813 // Note: There's only one orientation field for the whole file, in IFD0
1814 // The main image and any thumbnails therefore have the same orientation.
Ruben Brunk20796122015-07-21 17:51:54 -07001815 uint16_t orientation = nativeContext->getOrientation();
1816 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1817 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001818
Ruben Brunk47e91f22014-05-28 18:38:42 -07001819 }
1820
Ruben Brunk20796122015-07-21 17:51:54 -07001821 if (nativeContext->hasDescription()){
1822 // Set Description
1823 String8 description = nativeContext->getDescription();
1824 size_t len = description.bytes() + 1;
1825 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1826 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1827 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1828 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001829 }
1830 }
1831
Ruben Brunk20796122015-07-21 17:51:54 -07001832 if (nativeContext->hasGpsData()) {
1833 // Set GPS tags
1834 GpsData gpsData = nativeContext->getGpsData();
1835 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1836 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1837 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1838 TIFF_IFD_0);
1839 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1840 return nullptr;
1841 }
1842 }
1843
1844 {
1845 uint8_t version[] = {2, 3, 0, 0};
1846 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1847 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1848 }
1849
1850 {
1851 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1852 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1853 TAG_GPSLATITUDEREF, writer);
1854 }
1855
1856 {
1857 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1858 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1859 TAG_GPSLONGITUDEREF, writer);
1860 }
1861
1862 {
1863 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1864 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1865 }
1866
1867 {
1868 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1869 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1870 }
1871
1872 {
1873 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1874 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1875 }
1876
1877 {
1878 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1879 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1880 TAG_GPSDATESTAMP, writer);
1881 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001882 }
1883
Ruben Brunk47e91f22014-05-28 18:38:42 -07001884
Ruben Brunk20796122015-07-21 17:51:54 -07001885 if (nativeContext->hasThumbnail()) {
1886 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1887 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1888 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1889 TIFF_IFD_0);
1890 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1891 return nullptr;
1892 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001893 }
1894
1895 Vector<uint16_t> tagsToMove;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001896 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001897 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001898 tagsToMove.add(TAG_BITSPERSAMPLE);
1899 tagsToMove.add(TAG_COMPRESSION);
1900 tagsToMove.add(TAG_IMAGEWIDTH);
1901 tagsToMove.add(TAG_IMAGELENGTH);
1902 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1903 tagsToMove.add(TAG_BLACKLEVEL);
1904 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1905 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1906 tagsToMove.add(TAG_PLANARCONFIGURATION);
1907 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1908 tagsToMove.add(TAG_CFAPATTERN);
1909 tagsToMove.add(TAG_CFAPLANECOLOR);
1910 tagsToMove.add(TAG_CFALAYOUT);
1911 tagsToMove.add(TAG_XRESOLUTION);
1912 tagsToMove.add(TAG_YRESOLUTION);
1913 tagsToMove.add(TAG_RESOLUTIONUNIT);
1914 tagsToMove.add(TAG_WHITELEVEL);
1915 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001916 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1917 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1918 tagsToMove.add(TAG_OPCODELIST2);
Ruben Brunkfe816622015-06-16 23:03:09 -07001919 tagsToMove.add(TAG_OPCODELIST3);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001920
1921 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1922 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07001923 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001924 }
1925
Ruben Brunk20796122015-07-21 17:51:54 -07001926 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001927
Ruben Brunk20796122015-07-21 17:51:54 -07001928 {
1929 // Set photometric interpretation
1930 uint16_t interpretation = 2; // RGB
1931 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1932 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001933 }
Ruben Brunk20796122015-07-21 17:51:54 -07001934
1935 {
1936 // Set planar configuration
1937 uint16_t config = 1; // Chunky
1938 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1939 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
1940 }
1941
1942 {
1943 // Set samples per pixel
1944 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1945 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
1946 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
1947 }
1948
1949 {
1950 // Set bits per sample
Eino-Ville Talvala8069b1f2016-03-02 15:52:08 -08001951 uint16_t bits[SAMPLES_PER_RGB_PIXEL];
1952 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
1953 BAIL_IF_INVALID_RET_NULL_SP(
1954 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
Ruben Brunk20796122015-07-21 17:51:54 -07001955 env, TAG_BITSPERSAMPLE, writer);
1956 }
1957
1958 {
1959 // Set subfiletype
1960 uint32_t subfileType = 1; // Thumbnail image
1961 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1962 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
1963 }
1964
1965 {
1966 // Set compression
1967 uint16_t compression = 1; // None
1968 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1969 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
1970 }
1971
1972 {
1973 // Set dimensions
1974 uint32_t uWidth = nativeContext->getThumbnailWidth();
1975 uint32_t uHeight = nativeContext->getThumbnailHeight();
1976 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
1977 env, TAG_IMAGEWIDTH, writer);
1978 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
1979 env, TAG_IMAGELENGTH, writer);
1980 }
1981
1982 {
1983 // x resolution
1984 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1985 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1986 env, TAG_XRESOLUTION, writer);
1987
1988 // y resolution
1989 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1990 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1991 env, TAG_YRESOLUTION, writer);
1992
1993 uint16_t unit = 2; // inches
1994 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1995 env, TAG_RESOLUTIONUNIT, writer);
1996 }
1997 }
1998
1999 if (writer->addStrip(TIFF_IFD_0) != OK) {
2000 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
2001 jniThrowException(env, "java/lang/IllegalStateException",
2002 "Failed to setup thumbnail strip tags.");
2003 return nullptr;
2004 }
2005
2006 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002007 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
2008 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
2009 jniThrowException(env, "java/lang/IllegalStateException",
2010 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07002011 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07002012 }
2013 }
Ruben Brunk20796122015-07-21 17:51:54 -07002014 return writer;
2015}
2016
2017static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
2018 ALOGV("%s:", __FUNCTION__);
2019 DngCreator_setNativeContext(env, thiz, nullptr);
2020}
2021
2022static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
2023 ALOGV("%s:", __FUNCTION__);
2024
2025 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2026 if (context == nullptr) {
2027 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2028 jniThrowException(env, "java/lang/AssertionError",
2029 "setOrientation called with uninitialized DngCreator");
2030 return;
2031 }
2032
2033 uint16_t orientation = static_cast<uint16_t>(orient);
2034 context->setOrientation(orientation);
2035}
2036
2037static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
2038 ALOGV("%s:", __FUNCTION__);
2039
2040 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2041 if (context == nullptr) {
2042 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2043 jniThrowException(env, "java/lang/AssertionError",
2044 "setDescription called with uninitialized DngCreator");
2045 return;
2046 }
2047
2048 const char* desc = env->GetStringUTFChars(description, nullptr);
2049 context->setDescription(String8(desc));
2050 env->ReleaseStringUTFChars(description, desc);
2051}
2052
2053static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
2054 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
2055 ALOGV("%s:", __FUNCTION__);
2056
2057 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2058 if (context == nullptr) {
2059 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2060 jniThrowException(env, "java/lang/AssertionError",
2061 "setGpsTags called with uninitialized DngCreator");
2062 return;
2063 }
2064
2065 GpsData data;
2066
2067 jsize latLen = env->GetArrayLength(latTag);
2068 jsize longLen = env->GetArrayLength(longTag);
2069 jsize timeLen = env->GetArrayLength(timeTag);
2070 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2071 jniThrowException(env, "java/lang/IllegalArgumentException",
2072 "invalid latitude tag length");
2073 return;
2074 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2075 jniThrowException(env, "java/lang/IllegalArgumentException",
2076 "invalid longitude tag length");
2077 return;
2078 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2079 jniThrowException(env, "java/lang/IllegalArgumentException",
2080 "invalid time tag length");
2081 return;
2082 }
2083
2084 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2085 reinterpret_cast<jint*>(&data.mLatitude));
2086 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2087 reinterpret_cast<jint*>(&data.mLongitude));
2088 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2089 reinterpret_cast<jint*>(&data.mTimestamp));
2090
2091
2092 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2093 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2094 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2095 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2096 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2097 reinterpret_cast<char*>(&data.mDate));
2098 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2099
2100 context->setGpsData(data);
2101}
2102
2103static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2104 jint height) {
2105 ALOGV("%s:", __FUNCTION__);
2106
2107 NativeContext* context = DngCreator_getNativeContext(env, thiz);
2108 if (context == nullptr) {
2109 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2110 jniThrowException(env, "java/lang/AssertionError",
2111 "setThumbnail called with uninitialized DngCreator");
2112 return;
2113 }
2114
2115 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2116 jlong capacity = env->GetDirectBufferCapacity(buffer);
2117 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2118 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2119 "Invalid size %d for thumbnail, expected size was %d",
2120 capacity, fullSize);
2121 return;
2122 }
2123
2124 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2125 if (pixelBytes == nullptr) {
2126 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2127 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2128 return;
2129 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002130
2131 if (!context->setThumbnail(pixelBytes, width, height)) {
2132 jniThrowException(env, "java/lang/IllegalStateException",
2133 "Failed to set thumbnail.");
2134 return;
2135 }
2136}
2137
2138// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002139static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002140 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2141 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002142 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002143 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2144 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2145 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002146 uint32_t rStride = static_cast<uint32_t>(rowStride);
2147 uint32_t pStride = static_cast<uint32_t>(pixStride);
2148 uint32_t uWidth = static_cast<uint32_t>(width);
2149 uint32_t uHeight = static_cast<uint32_t>(height);
2150 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002151
2152 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2153 if(env->ExceptionCheck()) {
2154 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2155 return;
2156 }
2157
Ruben Brunk47e91f22014-05-28 18:38:42 -07002158 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002159 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002160 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2161 jniThrowException(env, "java/lang/AssertionError",
2162 "Write called with uninitialized DngCreator");
2163 return;
2164 }
Ruben Brunk20796122015-07-21 17:51:54 -07002165 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002166
Ruben Brunk20796122015-07-21 17:51:54 -07002167 if (writer.get() == nullptr) {
2168 return;
2169 }
2170
2171 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002172 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002173 return;
2174 }
2175
Ruben Brunk47e91f22014-05-28 18:38:42 -07002176 sp<JniInputByteBuffer> inBuf;
2177 Vector<StripSource*> sources;
2178 sp<DirectStripSource> thumbnailSource;
2179 uint32_t targetIfd = TIFF_IFD_0;
2180
2181 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2182
2183 if (hasThumbnail) {
2184 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2185 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2186 uint32_t thumbWidth = context->getThumbnailWidth();
2187 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2188 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2189 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2190 SAMPLES_PER_RGB_PIXEL);
2191 sources.add(thumbnailSource.get());
2192 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002193 }
2194
Ruben Brunk47e91f22014-05-28 18:38:42 -07002195 if (isDirect) {
2196 size_t fullSize = rStride * uHeight;
2197 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2198 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2199 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2200 "Invalid size %d for Image, size given in metadata is %d at current stride",
2201 capacity, fullSize);
2202 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002203 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002204
Ruben Brunk47e91f22014-05-28 18:38:42 -07002205 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002206 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002207 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2208 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2209 return;
2210 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002211
Ruben Brunk47e91f22014-05-28 18:38:42 -07002212 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2213 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2214 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2215 sources.add(&stripSource);
2216
2217 status_t ret = OK;
2218 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2219 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002220 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002221 jniThrowExceptionFmt(env, "java/io/IOException",
2222 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002223 }
2224 return;
2225 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002226 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002227 inBuf = new JniInputByteBuffer(env, inBuffer);
2228
2229 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2230 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2231 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2232 sources.add(&stripSource);
2233
2234 status_t ret = OK;
2235 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2236 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2237 if (!env->ExceptionCheck()) {
2238 jniThrowExceptionFmt(env, "java/io/IOException",
2239 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002240 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002241 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002242 }
2243 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002244}
2245
2246static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002247 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002248 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002249
2250 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2251 uint32_t pixStride = BYTES_PER_SAMPLE;
2252 uint32_t uWidth = static_cast<uint32_t>(width);
2253 uint32_t uHeight = static_cast<uint32_t>(height);
2254 uint64_t uOffset = static_cast<uint32_t>(offset);
2255
Dan Albert46d84442014-11-18 16:07:51 -08002256 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2257 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2258 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002259
2260 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002261 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002262 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2263 return;
2264 }
2265
Ruben Brunk47e91f22014-05-28 18:38:42 -07002266 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002267 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002268 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2269 jniThrowException(env, "java/lang/AssertionError",
2270 "Write called with uninitialized DngCreator");
2271 return;
2272 }
Ruben Brunk20796122015-07-21 17:51:54 -07002273 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002274
Ruben Brunk20796122015-07-21 17:51:54 -07002275 if (writer.get() == nullptr) {
2276 return;
2277 }
2278
2279 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002280 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002281 return;
2282 }
2283
2284 sp<DirectStripSource> thumbnailSource;
2285 uint32_t targetIfd = TIFF_IFD_0;
2286 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2287 Vector<StripSource*> sources;
2288
2289 if (hasThumbnail) {
2290 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2291 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2292 uint32_t width = context->getThumbnailWidth();
2293 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2294 width, context->getThumbnailHeight(), bytesPerPixel,
2295 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2296 SAMPLES_PER_RGB_PIXEL);
2297 sources.add(thumbnailSource.get());
2298 targetIfd = TIFF_IFD_SUB1;
2299 }
2300
2301 sp<JniInputStream> in = new JniInputStream(env, inStream);
2302
2303 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2304 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2305 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2306 sources.add(&stripSource);
2307
2308 status_t ret = OK;
2309 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2310 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2311 if (!env->ExceptionCheck()) {
2312 jniThrowExceptionFmt(env, "java/io/IOException",
2313 "Encountered error %d while writing file.", ret);
2314 }
2315 return;
2316 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002317}
2318
2319} /*extern "C" */
2320
Daniel Micay76f6a862015-09-19 17:31:01 -04002321static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07002322 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2323 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002324 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2325 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002326 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2327 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002328 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2329 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2330 (void*) DngCreator_nativeSetGpsTags},
2331 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2332 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002333 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002334 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002335 (void*) DngCreator_nativeWriteInputStream},
2336};
2337
Ruben Brunkb6079002014-05-22 12:33:54 -07002338int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002339 return RegisterMethodsOrDie(env,
2340 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002341}