blob: ba08237d98431a2c651a46c477ca3ef2142293f4 [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ruben Brunk20796122015-07-21 17:51:54 -070017#define LOG_NDEBUG 0
Ruben Brunkf967a542014-04-28 16:31:11 -070018#define LOG_TAG "DngCreator_JNI"
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <string.h>
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070021#include <algorithm>
22#include <memory>
Dan Albert46d84442014-11-18 16:07:51 -080023
24#include <utils/Log.h>
25#include <utils/Errors.h>
26#include <utils/StrongPointer.h>
27#include <utils/RefBase.h>
28#include <utils/Vector.h>
Ruben Brunk20796122015-07-21 17:51:54 -070029#include <utils/String8.h>
Dan Albert46d84442014-11-18 16:07:51 -080030#include <cutils/properties.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070031#include <system/camera_metadata.h>
32#include <camera/CameraMetadata.h>
33#include <img_utils/DngUtils.h>
34#include <img_utils/TagDefinitions.h>
35#include <img_utils/TiffIfd.h>
36#include <img_utils/TiffWriter.h>
37#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070038#include <img_utils/Input.h>
39#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070040
Andreas Gampeed6b9df2014-11-20 22:02:20 -080041#include "core_jni_helpers.h"
Ruben Brunkb8df8e02014-06-02 22:59:45 -070042
Ruben Brunkf967a542014-04-28 16:31:11 -070043#include "android_runtime/AndroidRuntime.h"
44#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
45
46#include <jni.h>
47#include <JNIHelp.h>
48
49using namespace android;
50using namespace img_utils;
51
Ruben Brunk20796122015-07-21 17:51:54 -070052#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070053 if ((expr) != OK) { \
54 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070055 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070056 return false; \
Ruben Brunkf967a542014-04-28 16:31:11 -070057 }
58
Ruben Brunk20796122015-07-21 17:51:54 -070059
60#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
61 if ((expr) != OK) { \
62 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
63 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
64 return nullptr; \
65 }
66
67
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -070068#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
69 if ((expr) != OK) { \
70 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
71 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
72 return -1; \
73 }
74
Ruben Brunk20796122015-07-21 17:51:54 -070075#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070076 if (entry.count == 0) { \
77 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070078 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunk20796122015-07-21 17:51:54 -070079 return nullptr; \
Ruben Brunkf967a542014-04-28 16:31:11 -070080 }
81
Ruben Brunk20796122015-07-21 17:51:54 -070082
Ruben Brunkb6079002014-05-22 12:33:54 -070083#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070084
85static struct {
86 jfieldID mNativeContext;
87} gDngCreatorClassInfo;
88
89static struct {
90 jmethodID mWriteMethod;
91} gOutputStreamClassInfo;
92
Ruben Brunk47e91f22014-05-28 18:38:42 -070093static struct {
94 jmethodID mReadMethod;
95 jmethodID mSkipMethod;
96} gInputStreamClassInfo;
97
98static struct {
99 jmethodID mGetMethod;
100} gInputByteBufferClassInfo;
101
Ruben Brunkf967a542014-04-28 16:31:11 -0700102enum {
103 BITS_PER_SAMPLE = 16,
104 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700105 BYTES_PER_RGB_PIXEL = 3,
106 BITS_PER_RGB_SAMPLE = 8,
107 BYTES_PER_RGB_SAMPLE = 1,
108 SAMPLES_PER_RGB_PIXEL = 3,
109 SAMPLES_PER_RAW_PIXEL = 1,
110 TIFF_IFD_0 = 0,
111 TIFF_IFD_SUB1 = 1,
112 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700113};
114
Ruben Brunk20796122015-07-21 17:51:54 -0700115
116/**
117 * POD container class for GPS tag data.
118 */
119class GpsData {
120public:
121 enum {
122 GPS_VALUE_LENGTH = 6,
123 GPS_REF_LENGTH = 2,
124 GPS_DATE_LENGTH = 11,
125 };
126
127 uint32_t mLatitude[GPS_VALUE_LENGTH];
128 uint32_t mLongitude[GPS_VALUE_LENGTH];
129 uint32_t mTimestamp[GPS_VALUE_LENGTH];
130 uint8_t mLatitudeRef[GPS_REF_LENGTH];
131 uint8_t mLongitudeRef[GPS_REF_LENGTH];
132 uint8_t mDate[GPS_DATE_LENGTH];
133};
134
Ruben Brunkf967a542014-04-28 16:31:11 -0700135// ----------------------------------------------------------------------------
136
Ruben Brunk47e91f22014-05-28 18:38:42 -0700137/**
138 * Container class for the persistent native context.
139 */
140
141class NativeContext : public LightRefBase<NativeContext> {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700142public:
Ruben Brunk20796122015-07-21 17:51:54 -0700143 enum {
144 DATETIME_COUNT = 20,
145 };
146
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700147 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700148 virtual ~NativeContext();
149
150 TiffWriter* getWriter();
151
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700152 std::shared_ptr<const CameraMetadata> getCharacteristics() const;
153 std::shared_ptr<const CameraMetadata> getResult() const;
154
Ruben Brunk20796122015-07-21 17:51:54 -0700155 uint32_t getThumbnailWidth() const;
156 uint32_t getThumbnailHeight() const;
157 const uint8_t* getThumbnail() const;
158 bool hasThumbnail() const;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700159
160 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
161
Ruben Brunk20796122015-07-21 17:51:54 -0700162 void setOrientation(uint16_t orientation);
163 uint16_t getOrientation() const;
164
165 void setDescription(const String8& desc);
166 String8 getDescription() const;
167 bool hasDescription() const;
168
169 void setGpsData(const GpsData& data);
170 GpsData getGpsData() const;
171 bool hasGpsData() const;
172
173 void setCaptureTime(const String8& formattedCaptureTime);
174 String8 getCaptureTime() const;
175 bool hasCaptureTime() const;
176
Ruben Brunk47e91f22014-05-28 18:38:42 -0700177private:
178 Vector<uint8_t> mCurrentThumbnail;
179 TiffWriter mWriter;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700180 std::shared_ptr<CameraMetadata> mCharacteristics;
181 std::shared_ptr<CameraMetadata> mResult;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700182 uint32_t mThumbnailWidth;
183 uint32_t mThumbnailHeight;
Ruben Brunk20796122015-07-21 17:51:54 -0700184 uint16_t mOrientation;
185 bool mThumbnailSet;
186 bool mGpsSet;
187 bool mDescriptionSet;
188 bool mCaptureTimeSet;
189 String8 mDescription;
190 GpsData mGpsData;
191 String8 mFormattedCaptureTime;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700192};
193
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700194NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
195 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
196 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
Ruben Brunk20796122015-07-21 17:51:54 -0700197 mThumbnailHeight(0), mOrientation(0), mThumbnailSet(false), mGpsSet(false),
198 mDescriptionSet(false), mCaptureTimeSet(false) {}
Ruben Brunk47e91f22014-05-28 18:38:42 -0700199
200NativeContext::~NativeContext() {}
201
202TiffWriter* NativeContext::getWriter() {
203 return &mWriter;
204}
205
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700206std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const {
207 return mCharacteristics;
208}
209
210std::shared_ptr<const CameraMetadata> NativeContext::getResult() const {
211 return mResult;
212}
213
Ruben Brunk20796122015-07-21 17:51:54 -0700214uint32_t NativeContext::getThumbnailWidth() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700215 return mThumbnailWidth;
216}
217
Ruben Brunk20796122015-07-21 17:51:54 -0700218uint32_t NativeContext::getThumbnailHeight() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700219 return mThumbnailHeight;
220}
221
Ruben Brunk20796122015-07-21 17:51:54 -0700222const uint8_t* NativeContext::getThumbnail() const {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700223 return mCurrentThumbnail.array();
224}
225
Ruben Brunk20796122015-07-21 17:51:54 -0700226bool NativeContext::hasThumbnail() const {
227 return mThumbnailSet;
228}
229
Ruben Brunk47e91f22014-05-28 18:38:42 -0700230bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
231 mThumbnailWidth = width;
232 mThumbnailHeight = height;
233
234 size_t size = BYTES_PER_RGB_PIXEL * width * height;
235 if (mCurrentThumbnail.resize(size) < 0) {
236 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
237 return false;
238 }
239
240 uint8_t* thumb = mCurrentThumbnail.editArray();
241 memcpy(thumb, buffer, size);
Ruben Brunk20796122015-07-21 17:51:54 -0700242 mThumbnailSet = true;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700243 return true;
244}
245
Ruben Brunk20796122015-07-21 17:51:54 -0700246void NativeContext::setOrientation(uint16_t orientation) {
247 mOrientation = orientation;
248}
249
250uint16_t NativeContext::getOrientation() const {
251 return mOrientation;
252}
253
254void NativeContext::setDescription(const String8& desc) {
255 mDescription = desc;
256 mDescriptionSet = true;
257}
258
259String8 NativeContext::getDescription() const {
260 return mDescription;
261}
262
263bool NativeContext::hasDescription() const {
264 return mDescriptionSet;
265}
266
267void NativeContext::setGpsData(const GpsData& data) {
268 mGpsData = data;
269 mGpsSet = true;
270}
271
272GpsData NativeContext::getGpsData() const {
273 return mGpsData;
274}
275
276bool NativeContext::hasGpsData() const {
277 return mGpsSet;
278}
279
280void NativeContext::setCaptureTime(const String8& formattedCaptureTime) {
281 mFormattedCaptureTime = formattedCaptureTime;
282 mCaptureTimeSet = true;
283}
284
285String8 NativeContext::getCaptureTime() const {
286 return mFormattedCaptureTime;
287}
288
289bool NativeContext::hasCaptureTime() const {
290 return mCaptureTimeSet;
291}
292
Ruben Brunk47e91f22014-05-28 18:38:42 -0700293// End of NativeContext
294// ----------------------------------------------------------------------------
295
296/**
297 * Wrapper class for a Java OutputStream.
298 *
299 * This class is not intended to be used across JNI calls.
300 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700301class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
302public:
303 JniOutputStream(JNIEnv* env, jobject outStream);
304
305 virtual ~JniOutputStream();
306
307 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700308
Ruben Brunkf967a542014-04-28 16:31:11 -0700309 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700310
Ruben Brunkf967a542014-04-28 16:31:11 -0700311 status_t close();
312private:
313 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700314 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700315 };
316 jobject mOutputStream;
317 JNIEnv* mEnv;
318 jbyteArray mByteArray;
319};
320
321JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
322 mEnv(env) {
323 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700324 if (mByteArray == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700325 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
326 }
327}
328
329JniOutputStream::~JniOutputStream() {
330 mEnv->DeleteLocalRef(mByteArray);
331}
332
333status_t JniOutputStream::open() {
334 // Do nothing
335 return OK;
336}
337
338status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
339 while(count > 0) {
340 size_t len = BYTE_ARRAY_LENGTH;
341 len = (count > len) ? len : count;
342 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
343
344 if (mEnv->ExceptionCheck()) {
345 return BAD_VALUE;
346 }
347
348 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
349 0, len);
350
351 if (mEnv->ExceptionCheck()) {
352 return BAD_VALUE;
353 }
354
355 count -= len;
356 offset += len;
357 }
358 return OK;
359}
360
361status_t JniOutputStream::close() {
362 // Do nothing
363 return OK;
364}
365
Ruben Brunk47e91f22014-05-28 18:38:42 -0700366// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700367// ----------------------------------------------------------------------------
368
Ruben Brunk47e91f22014-05-28 18:38:42 -0700369/**
370 * Wrapper class for a Java InputStream.
371 *
372 * This class is not intended to be used across JNI calls.
373 */
374class JniInputStream : public Input, public LightRefBase<JniInputStream> {
375public:
376 JniInputStream(JNIEnv* env, jobject inStream);
377
378 status_t open();
379
380 status_t close();
381
382 ssize_t read(uint8_t* buf, size_t offset, size_t count);
383
384 ssize_t skip(size_t count);
385
386 virtual ~JniInputStream();
387private:
388 enum {
389 BYTE_ARRAY_LENGTH = 4096
390 };
391 jobject mInStream;
392 JNIEnv* mEnv;
393 jbyteArray mByteArray;
394
395};
396
397JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
398 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700399 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700400 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
401 }
402}
403
404JniInputStream::~JniInputStream() {
405 mEnv->DeleteLocalRef(mByteArray);
406}
407
408ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
409
410 jint realCount = BYTE_ARRAY_LENGTH;
411 if (count < BYTE_ARRAY_LENGTH) {
412 realCount = count;
413 }
414 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
415 realCount);
416
417 if (actual < 0) {
418 return NOT_ENOUGH_DATA;
419 }
420
421 if (mEnv->ExceptionCheck()) {
422 return BAD_VALUE;
423 }
424
425 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
426 if (mEnv->ExceptionCheck()) {
427 return BAD_VALUE;
428 }
429 return actual;
430}
431
432ssize_t JniInputStream::skip(size_t count) {
433 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
434 static_cast<jlong>(count));
435
436 if (mEnv->ExceptionCheck()) {
437 return BAD_VALUE;
438 }
439 if (actual < 0) {
440 return NOT_ENOUGH_DATA;
441 }
442 return actual;
443}
444
445status_t JniInputStream::open() {
446 // Do nothing
447 return OK;
448}
449
450status_t JniInputStream::close() {
451 // Do nothing
452 return OK;
453}
454
455// End of JniInputStream
456// ----------------------------------------------------------------------------
457
458/**
459 * Wrapper class for a non-direct Java ByteBuffer.
460 *
461 * This class is not intended to be used across JNI calls.
462 */
463class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
464public:
465 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
466
467 status_t open();
468
469 status_t close();
470
471 ssize_t read(uint8_t* buf, size_t offset, size_t count);
472
473 virtual ~JniInputByteBuffer();
474private:
475 enum {
476 BYTE_ARRAY_LENGTH = 4096
477 };
478 jobject mInBuf;
479 JNIEnv* mEnv;
480 jbyteArray mByteArray;
481};
482
483JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
484 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -0700485 if (mByteArray == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700486 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
487 }
488}
489
490JniInputByteBuffer::~JniInputByteBuffer() {
491 mEnv->DeleteLocalRef(mByteArray);
492}
493
494ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
495 jint realCount = BYTE_ARRAY_LENGTH;
496 if (count < BYTE_ARRAY_LENGTH) {
497 realCount = count;
498 }
499
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700500 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
501 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800502 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700503
504 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800505 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700506 return BAD_VALUE;
507 }
508
509 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
510 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800511 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700512 return BAD_VALUE;
513 }
514 return realCount;
515}
516
517status_t JniInputByteBuffer::open() {
518 // Do nothing
519 return OK;
520}
521
522status_t JniInputByteBuffer::close() {
523 // Do nothing
524 return OK;
525}
526
527// End of JniInputByteBuffer
528// ----------------------------------------------------------------------------
529
530/**
531 * StripSource subclass for Input types.
532 *
533 * This class is not intended to be used across JNI calls.
534 */
535
536class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
537public:
538 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
539 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
540 uint32_t samplesPerPixel);
541
542 virtual ~InputStripSource();
543
544 virtual status_t writeToStream(Output& stream, uint32_t count);
545
546 virtual uint32_t getIfd() const;
547protected:
548 uint32_t mIfd;
549 Input* mInput;
550 uint32_t mWidth;
551 uint32_t mHeight;
552 uint32_t mPixStride;
553 uint32_t mRowStride;
554 uint64_t mOffset;
555 JNIEnv* mEnv;
556 uint32_t mBytesPerSample;
557 uint32_t mSamplesPerPixel;
558};
559
560InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
561 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
562 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
563 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
564 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
565 mSamplesPerPixel(samplesPerPixel) {}
566
567InputStripSource::~InputStripSource() {}
568
569status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700570 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700571 jlong offset = mOffset;
572
573 if (fullSize != count) {
574 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
575 fullSize);
576 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
577 return BAD_VALUE;
578 }
579
580 // Skip offset
581 while (offset > 0) {
582 ssize_t skipped = mInput->skip(offset);
583 if (skipped <= 0) {
584 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
585 jniThrowExceptionFmt(mEnv, "java/io/IOException",
586 "Early EOF encountered in skip, not enough pixel data for image of size %u",
587 fullSize);
588 skipped = NOT_ENOUGH_DATA;
589 } else {
590 if (!mEnv->ExceptionCheck()) {
591 jniThrowException(mEnv, "java/io/IOException",
592 "Error encountered while skip bytes in input stream.");
593 }
594 }
595
596 return skipped;
597 }
598 offset -= skipped;
599 }
600
601 Vector<uint8_t> row;
602 if (row.resize(mRowStride) < 0) {
603 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
604 return BAD_VALUE;
605 }
606
607 uint8_t* rowBytes = row.editArray();
608
609 for (uint32_t i = 0; i < mHeight; ++i) {
610 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800611 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700612
613 while (rowFillAmt < mRowStride) {
614 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
615 if (bytesRead <= 0) {
616 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800617 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
618 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700619 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800620 "Early EOF encountered, not enough pixel data for image of size %"
621 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700622 bytesRead = NOT_ENOUGH_DATA;
623 } else {
624 if (!mEnv->ExceptionCheck()) {
625 jniThrowException(mEnv, "java/io/IOException",
626 "Error encountered while reading");
627 }
628 }
629 return bytesRead;
630 }
631 rowFillAmt += bytesRead;
632 rowSize -= bytesRead;
633 }
634
635 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
636 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
637
638 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
639 mEnv->ExceptionCheck()) {
640 if (!mEnv->ExceptionCheck()) {
641 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
642 }
643 return BAD_VALUE;
644 }
645 } else {
646 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
647 jniThrowException(mEnv, "java/lang/IllegalStateException",
648 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
649 return BAD_VALUE;
650
651 // TODO: Add support for non-contiguous pixels if needed.
652 }
653 }
654 return OK;
655}
656
657uint32_t InputStripSource::getIfd() const {
658 return mIfd;
659}
660
661// End of InputStripSource
662// ----------------------------------------------------------------------------
663
664/**
665 * StripSource subclass for direct buffer types.
666 *
667 * This class is not intended to be used across JNI calls.
668 */
669
670class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
671public:
672 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
673 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
674 uint32_t bytesPerSample, uint32_t samplesPerPixel);
675
676 virtual ~DirectStripSource();
677
678 virtual status_t writeToStream(Output& stream, uint32_t count);
679
680 virtual uint32_t getIfd() const;
681protected:
682 uint32_t mIfd;
683 const uint8_t* mPixelBytes;
684 uint32_t mWidth;
685 uint32_t mHeight;
686 uint32_t mPixStride;
687 uint32_t mRowStride;
688 uint16_t mOffset;
689 JNIEnv* mEnv;
690 uint32_t mBytesPerSample;
691 uint32_t mSamplesPerPixel;
692};
693
694DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
695 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
696 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
697 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
698 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
699 mSamplesPerPixel(samplesPerPixel) {}
700
701DirectStripSource::~DirectStripSource() {}
702
703status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700704 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700705
706 if (fullSize != count) {
707 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
708 fullSize);
709 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
710 return BAD_VALUE;
711 }
712
Ruben Brunk20796122015-07-21 17:51:54 -0700713
Ruben Brunk47e91f22014-05-28 18:38:42 -0700714 if (mPixStride == mBytesPerSample * mSamplesPerPixel
715 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
716 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
717
718 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
719 if (!mEnv->ExceptionCheck()) {
720 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
721 }
722 return BAD_VALUE;
723 }
724 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
725 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
726
727 for (size_t i = 0; i < mHeight; ++i) {
728 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
729 mEnv->ExceptionCheck()) {
730 if (!mEnv->ExceptionCheck()) {
731 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
732 }
733 return BAD_VALUE;
734 }
735 }
736 } else {
737 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
738
739 jniThrowException(mEnv, "java/lang/IllegalStateException",
740 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
741 return BAD_VALUE;
742
743 // TODO: Add support for non-contiguous pixels if needed.
744 }
745 return OK;
746
747}
748
749uint32_t DirectStripSource::getIfd() const {
750 return mIfd;
751}
752
753// End of DirectStripSource
754// ----------------------------------------------------------------------------
755
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700756/**
Ruben Brunk20796122015-07-21 17:51:54 -0700757 * Given a buffer crop rectangle relative to the pixel array size, and the pre-correction active
758 * array crop rectangle for the camera characteristics, set the default crop rectangle in the
759 * TiffWriter relative to the buffer crop rectangle origin.
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700760 */
761static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics,
Ruben Brunk20796122015-07-21 17:51:54 -0700762 uint32_t bufWidth, uint32_t bufHeight, sp<TiffWriter> writer) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700763
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700764 camera_metadata_ro_entry entry =
Ruben Brunk20796122015-07-21 17:51:54 -0700765 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700766 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
767 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
768 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
769 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
770
Ruben Brunk20796122015-07-21 17:51:54 -0700771 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
772
773 // Crop based on pre-correction array for pixel array
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700774 uint32_t aLeft = xmin;
775 uint32_t aTop = ymin;
776 uint32_t aRight = xmin + width;
777 uint32_t aBottom = ymin + height;
778
Ruben Brunk20796122015-07-21 17:51:54 -0700779 // 8 pixel border crop for pixel array dimens
780 uint32_t bLeft = margin;
781 uint32_t bTop = margin;
782 uint32_t bRight = bufWidth - margin;
783 uint32_t bBottom = bufHeight - margin;
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700784
Ruben Brunk20796122015-07-21 17:51:54 -0700785 // Set the crop to be the intersection of the two rectangles
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700786 uint32_t defaultCropOrigin[] = {std::max(aLeft, bLeft), std::max(aTop, bTop)};
787 uint32_t defaultCropSize[] = {std::min(aRight, bRight) - defaultCropOrigin[0],
788 std::min(aBottom, bBottom) - defaultCropOrigin[1]};
789
Ruben Brunk20796122015-07-21 17:51:54 -0700790 // If using buffers with pre-correction array dimens, switch to 8 pixel border crop
791 // relative to the pixel array dimens
792 if (bufWidth == width && bufHeight == height) {
793 defaultCropOrigin[0] = xmin + margin;
794 defaultCropOrigin[1] = ymin + margin;
795 defaultCropSize[0] = width - margin;
796 defaultCropSize[1] = height - margin;
797 }
798
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700799 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
800 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
801 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
802 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
803
804 return OK;
805}
806
Ruben Brunk20796122015-07-21 17:51:54 -0700807static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer,
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700808 const CameraMetadata& characteristics, jint width, jint height) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700809 if (width <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700810 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700811 "Image width %d is invalid", width);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700812 return false;
813 }
814
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700815 if (height <= 0) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700816 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700817 "Image height %d is invalid", height);
818 return false;
819 }
820
821 camera_metadata_ro_entry preCorrectionEntry =
822 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
823 camera_metadata_ro_entry pixelArrayEntry =
824 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
825
826 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]);
827 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]);
828 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]);
829 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]);
830
831 bool matchesPixelArray = (pWidth == width && pHeight == height);
832 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height);
833
Ruben Brunk20796122015-07-21 17:51:54 -0700834 if (!(matchesPixelArray || matchesPreCorrectionArray)) {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -0700835 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
836 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel "
837 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)",
838 width, height, pWidth, pHeight, cWidth, cHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700839 return false;
840 }
841
842 return true;
843}
844
Ruben Brunk20796122015-07-21 17:51:54 -0700845static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700846 const Vector<uint16_t>& entries) {
847 for (size_t i = 0; i < entries.size(); ++i) {
848 uint16_t tagId = entries[i];
849 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
Ruben Brunk20796122015-07-21 17:51:54 -0700850 if (entry.get() == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700851 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
852 ifdFrom);
853 return BAD_VALUE;
854 }
855 if (writer->addEntry(entry, ifdTo) != OK) {
856 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
857 ifdFrom);
858 return BAD_VALUE;
859 }
860 writer->removeEntry(tagId, ifdFrom);
861 }
862 return OK;
863}
864
Ruben Brunkd70132c2014-08-22 16:24:49 -0700865/**
866 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
867 * Returns OK on success, or a negative error code if the CFA enum was invalid.
868 */
869static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
870 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
871 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
872 cfaEnum);
873 switch(cfa) {
874 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
875 cfaOut[0] = 0;
876 cfaOut[1] = 1;
877 cfaOut[2] = 1;
878 cfaOut[3] = 2;
879 break;
880 }
881 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
882 cfaOut[0] = 1;
883 cfaOut[1] = 0;
884 cfaOut[2] = 2;
885 cfaOut[3] = 1;
886 break;
887 }
888 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
889 cfaOut[0] = 1;
890 cfaOut[1] = 2;
891 cfaOut[2] = 0;
892 cfaOut[3] = 1;
893 break;
894 }
895 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
896 cfaOut[0] = 2;
897 cfaOut[1] = 1;
898 cfaOut[2] = 1;
899 cfaOut[3] = 0;
900 break;
901 }
902 default: {
903 return BAD_VALUE;
904 }
905 }
906 return OK;
907}
908
909/**
910 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
911 * RGGB for an unknown enum.
912 */
913static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
914 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
915 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
916 cfaEnum);
917 switch(cfa) {
918 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
919 return OpcodeListBuilder::CFA_RGGB;
920 }
921 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
922 return OpcodeListBuilder::CFA_GRBG;
923 }
924 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
925 return OpcodeListBuilder::CFA_GBRG;
926 }
927 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
928 return OpcodeListBuilder::CFA_BGGR;
929 }
930 default: {
931 return OpcodeListBuilder::CFA_RGGB;
932 }
933 }
934}
935
936/**
937 * For each color plane, find the corresponding noise profile coefficients given in the
938 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
939 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
940 *
941 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
942 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
943 * coefficients.
944 * numChannels - the number of noise profile coefficient pairs and color channels given in
945 * the perChannelNoiseProfile and cfa arguments, respectively.
946 * planeColors - the color planes in the noise profile output.
947 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
948 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
949 *
950 * returns OK, or a negative error code on failure.
951 */
952static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
953 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
954 /*out*/double* noiseProfile) {
955
956 for (size_t p = 0; p < numPlanes; ++p) {
957 size_t S = p * 2;
958 size_t O = p * 2 + 1;
959
960 noiseProfile[S] = 0;
961 noiseProfile[O] = 0;
962 bool uninitialized = true;
963 for (size_t c = 0; c < numChannels; ++c) {
964 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
965 noiseProfile[S] = perChannelNoiseProfile[c * 2];
966 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
967 uninitialized = false;
968 }
969 }
970 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800971 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
972 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700973 return BAD_VALUE;
974 }
975 }
976 return OK;
977}
978
Ruben Brunk47e91f22014-05-28 18:38:42 -0700979// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700980extern "C" {
981
Ruben Brunk47e91f22014-05-28 18:38:42 -0700982static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700983 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700984 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700985 gDngCreatorClassInfo.mNativeContext));
986}
987
Ruben Brunk47e91f22014-05-28 18:38:42 -0700988static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700989 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700990 NativeContext* current = DngCreator_getNativeContext(env, thiz);
991
Ruben Brunk20796122015-07-21 17:51:54 -0700992 if (context != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700993 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700994 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700995
Ruben Brunkf967a542014-04-28 16:31:11 -0700996 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700997 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700998 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700999
Ruben Brunkf967a542014-04-28 16:31:11 -07001000 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001001 reinterpret_cast<jlong>(context.get()));
1002}
1003
Ruben Brunkf967a542014-04-28 16:31:11 -07001004static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
1005 ALOGV("%s:", __FUNCTION__);
1006
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001007 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
1008 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -07001009
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001010 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
1011 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
1012 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001013
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001014 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
1015 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
1016 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -07001017
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001018 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
1019 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
1020 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -07001021}
1022
1023static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001024 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001025 ALOGV("%s:", __FUNCTION__);
1026 CameraMetadata characteristics;
1027 CameraMetadata results;
1028 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
1029 jniThrowException(env, "java/lang/AssertionError",
1030 "No native metadata defined for camera characteristics.");
1031 return;
1032 }
1033 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
1034 jniThrowException(env, "java/lang/AssertionError",
1035 "No native metadata defined for capture results.");
1036 return;
1037 }
1038
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001039 sp<NativeContext> nativeContext = new NativeContext(characteristics, results);
Ruben Brunk20796122015-07-21 17:51:54 -07001040
1041 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr);
1042
1043 size_t len = strlen(captureTime) + 1;
1044 if (len != NativeContext::DATETIME_COUNT) {
1045 jniThrowException(env, "java/lang/IllegalArgumentException",
1046 "Formatted capture time string length is not required 20 characters");
1047 return;
1048 }
1049
1050 nativeContext->setCaptureTime(String8(captureTime));
1051
1052 DngCreator_setNativeContext(env, thiz, nativeContext);
1053}
1054
1055static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth,
1056 uint32_t imageHeight) {
1057
1058 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz);
1059
1060 if (nativeContext == nullptr) {
1061 jniThrowException(env, "java/lang/AssertionError",
1062 "No native context, must call init before other operations.");
1063 return nullptr;
1064 }
1065
1066 CameraMetadata characteristics = *(nativeContext->getCharacteristics());
1067 CameraMetadata results = *(nativeContext->getResult());
1068
1069 sp<TiffWriter> writer = new TiffWriter();
1070
1071 uint32_t preWidth = 0;
1072 uint32_t preHeight = 0;
1073 {
1074 // Check dimensions
1075 camera_metadata_entry entry =
1076 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1077 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1078 preWidth = static_cast<uint32_t>(entry.data.i32[2]);
1079 preHeight = static_cast<uint32_t>(entry.data.i32[3]);
1080
1081 camera_metadata_entry pixelArrayEntry =
1082 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE);
1083 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]);
1084 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]);
1085
1086 if (!((imageWidth == preWidth && imageHeight == preHeight) ||
1087 (imageWidth == pixWidth && imageHeight == pixHeight))) {
1088 jniThrowException(env, "java/lang/AssertionError",
1089 "Height and width of imate buffer did not match height and width of"
1090 "either the preCorrectionActiveArraySize or the pixelArraySize.");
1091 return nullptr;
1092 }
1093 }
1094
1095
Ruben Brunkf967a542014-04-28 16:31:11 -07001096
1097 writer->addIfd(TIFF_IFD_0);
1098
1099 status_t err = OK;
1100
1101 const uint32_t samplesPerPixel = 1;
1102 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -07001103
1104 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001105 uint8_t cfaPlaneColor[3] = {0, 1, 2};
1106 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001107
1108 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -07001109 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001110
1111 // Setup main image tags
1112
Ruben Brunkf967a542014-04-28 16:31:11 -07001113 {
1114 // Set orientation
1115 uint16_t orientation = 1; // Normal
Ruben Brunk20796122015-07-21 17:51:54 -07001116 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1117 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001118 }
1119
1120 {
1121 // Set subfiletype
1122 uint32_t subfileType = 0; // Main image
Ruben Brunk20796122015-07-21 17:51:54 -07001123 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1124 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001125 }
1126
1127 {
1128 // Set bits per sample
1129 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
Ruben Brunk20796122015-07-21 17:51:54 -07001130 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001131 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001132 }
1133
1134 {
1135 // Set compression
1136 uint16_t compression = 1; // None
Ruben Brunk20796122015-07-21 17:51:54 -07001137 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1138 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001139 }
1140
1141 {
1142 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001143 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0),
1144 env, TAG_IMAGEWIDTH, writer);
1145 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0),
1146 env, TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001147 }
1148
1149 {
1150 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -07001151 uint16_t interpretation = 32803; // CFA
Ruben Brunk20796122015-07-21 17:51:54 -07001152 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1153 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001154 }
1155
1156 {
1157 // Set blacklevel tags
1158 camera_metadata_entry entry =
1159 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk20796122015-07-21 17:51:54 -07001160 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001161 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
Ruben Brunk20796122015-07-21 17:51:54 -07001162 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel,
1163 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001164
1165 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001166 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
1167 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001168 }
1169
1170 {
1171 // Set samples per pixel
1172 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
Ruben Brunk20796122015-07-21 17:51:54 -07001173 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001174 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001175 }
1176
1177 {
1178 // Set planar configuration
1179 uint16_t config = 1; // Chunky
Ruben Brunk20796122015-07-21 17:51:54 -07001180 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1181 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001182 }
1183
1184 {
1185 // Set CFA pattern dimensions
1186 uint16_t repeatDim[2] = {2, 2};
Ruben Brunk20796122015-07-21 17:51:54 -07001187 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
1188 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001189 }
1190
1191 {
1192 // Set CFA pattern
1193 camera_metadata_entry entry =
1194 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk20796122015-07-21 17:51:54 -07001195 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001196
1197 const int cfaLength = 4;
1198 cfaEnum = entry.data.u8[0];
1199 uint8_t cfa[cfaLength];
1200 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
1201 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1202 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -07001203 }
Ruben Brunkd70132c2014-08-22 16:24:49 -07001204
Ruben Brunk20796122015-07-21 17:51:54 -07001205 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0),
1206 env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001207
1208 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -07001209 }
1210
1211 {
1212 // Set CFA plane color
Ruben Brunk20796122015-07-21 17:51:54 -07001213 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
1214 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001215 }
1216
1217 {
1218 // Set CFA layout
1219 uint16_t cfaLayout = 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001220 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001221 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001222 }
1223
1224 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001225 // image description
1226 uint8_t imageDescription = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001227 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription,
1228 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001229 }
1230
1231 {
1232 // make
1233 char manufacturer[PROPERTY_VALUE_MAX];
1234
1235 // Use "" to represent unknown make as suggested in TIFF/EP spec.
1236 property_get("ro.product.manufacturer", manufacturer, "");
1237 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1238
Ruben Brunk20796122015-07-21 17:51:54 -07001239 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count,
1240 reinterpret_cast<uint8_t*>(manufacturer), TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001241 }
1242
1243 {
1244 // model
1245 char model[PROPERTY_VALUE_MAX];
1246
1247 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1248 property_get("ro.product.model", model, "");
1249 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1250
Ruben Brunk20796122015-07-21 17:51:54 -07001251 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count,
1252 reinterpret_cast<uint8_t*>(model), TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001253 }
1254
1255 {
1256 // x resolution
1257 uint32_t xres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001258 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001259 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001260
1261 // y resolution
1262 uint32_t yres[] = { 72, 1 }; // default 72 ppi
Ruben Brunk20796122015-07-21 17:51:54 -07001263 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001264 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001265
1266 uint16_t unit = 2; // inches
Ruben Brunk20796122015-07-21 17:51:54 -07001267 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001268 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001269 }
1270
1271 {
1272 // software
1273 char software[PROPERTY_VALUE_MAX];
1274 property_get("ro.build.fingerprint", software, "");
1275 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
Ruben Brunk20796122015-07-21 17:51:54 -07001276 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count,
1277 reinterpret_cast<uint8_t*>(software), TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001278 }
1279
Ruben Brunk20796122015-07-21 17:51:54 -07001280 if (nativeContext->hasCaptureTime()) {
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001281 // datetime
Ruben Brunk20796122015-07-21 17:51:54 -07001282 String8 captureTime = nativeContext->getCaptureTime();
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001283
Ruben Brunk20796122015-07-21 17:51:54 -07001284 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
1285 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001286 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1287 "Invalid metadata for tag %x", TAG_DATETIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001288 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001289 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001290
1291 // datetime original
Ruben Brunk20796122015-07-21 17:51:54 -07001292 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
1293 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001294 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1295 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
Ruben Brunk20796122015-07-21 17:51:54 -07001296 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001297 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001298 }
1299
1300 {
1301 // TIFF/EP standard id
1302 uint8_t standardId[] = { 1, 0, 0, 0 };
Ruben Brunk20796122015-07-21 17:51:54 -07001303 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001304 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001305 }
1306
1307 {
1308 // copyright
1309 uint8_t copyright = '\0'; // empty
Ruben Brunk20796122015-07-21 17:51:54 -07001310 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001311 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001312 }
1313
1314 {
1315 // exposure time
1316 camera_metadata_entry entry =
1317 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk20796122015-07-21 17:51:54 -07001318 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001319
1320 int64_t exposureTime = *(entry.data.i64);
1321
1322 if (exposureTime < 0) {
1323 // Should be unreachable
1324 jniThrowException(env, "java/lang/IllegalArgumentException",
1325 "Negative exposure time in metadata");
Ruben Brunk20796122015-07-21 17:51:54 -07001326 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001327 }
1328
1329 // Ensure exposure time doesn't overflow (for exposures > 4s)
1330 uint32_t denominator = 1000000000;
1331 while (exposureTime > UINT32_MAX) {
1332 exposureTime >>= 1;
1333 denominator >>= 1;
1334 if (denominator == 0) {
1335 // Should be unreachable
1336 jniThrowException(env, "java/lang/IllegalArgumentException",
1337 "Exposure time too long");
Ruben Brunk20796122015-07-21 17:51:54 -07001338 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001339 }
1340 }
1341
1342 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
Ruben Brunk20796122015-07-21 17:51:54 -07001343 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001344 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001345
1346 }
1347
1348 {
1349 // ISO speed ratings
1350 camera_metadata_entry entry =
1351 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk20796122015-07-21 17:51:54 -07001352 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001353
1354 int32_t tempIso = *(entry.data.i32);
1355 if (tempIso < 0) {
1356 jniThrowException(env, "java/lang/IllegalArgumentException",
1357 "Negative ISO value");
Ruben Brunk20796122015-07-21 17:51:54 -07001358 return nullptr;
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001359 }
1360
1361 if (tempIso > UINT16_MAX) {
1362 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1363 tempIso = UINT16_MAX;
1364 }
1365
1366 uint16_t iso = static_cast<uint16_t>(tempIso);
Ruben Brunk20796122015-07-21 17:51:54 -07001367 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001368 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001369 }
1370
1371 {
1372 // focal length
1373 camera_metadata_entry entry =
1374 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk20796122015-07-21 17:51:54 -07001375 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001376
1377 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001378 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001379 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001380 }
1381
1382 {
1383 // f number
1384 camera_metadata_entry entry =
1385 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk20796122015-07-21 17:51:54 -07001386 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001387
1388 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
Ruben Brunk20796122015-07-21 17:51:54 -07001389 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001390 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001391 }
1392
1393 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001394 // Set DNG version information
1395 uint8_t version[4] = {1, 4, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001396 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001397 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001398
1399 uint8_t backwardVersion[4] = {1, 1, 0, 0};
Ruben Brunk20796122015-07-21 17:51:54 -07001400 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion,
1401 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001402 }
1403
1404 {
1405 // Set whitelevel
1406 camera_metadata_entry entry =
1407 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk20796122015-07-21 17:51:54 -07001408 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001409 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
Ruben Brunk20796122015-07-21 17:51:54 -07001410 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0),
1411 env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001412 }
1413
1414 {
1415 // Set default scale
1416 uint32_t defaultScale[4] = {1, 1, 1, 1};
Ruben Brunk20796122015-07-21 17:51:54 -07001417 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale,
1418 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001419 }
1420
1421 bool singleIlluminant = false;
1422 {
1423 // Set calibration illuminants
1424 camera_metadata_entry entry1 =
1425 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk20796122015-07-21 17:51:54 -07001426 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001427 camera_metadata_entry entry2 =
1428 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1429 if (entry2.count == 0) {
1430 singleIlluminant = true;
1431 }
1432 uint16_t ref1 = entry1.data.u8[0];
1433
Ruben Brunk20796122015-07-21 17:51:54 -07001434 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001435 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001436
1437 if (!singleIlluminant) {
1438 uint16_t ref2 = entry2.data.u8[0];
Ruben Brunk20796122015-07-21 17:51:54 -07001439 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001440 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001441 }
1442 }
1443
1444 {
1445 // Set color transforms
1446 camera_metadata_entry entry1 =
1447 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001448 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001449
1450 int32_t colorTransform1[entry1.count * 2];
1451
1452 size_t ctr = 0;
1453 for(size_t i = 0; i < entry1.count; ++i) {
1454 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1455 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1456 }
1457
Ruben Brunk20796122015-07-21 17:51:54 -07001458 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count,
1459 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001460
1461 if (!singleIlluminant) {
1462 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001463 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001464 int32_t colorTransform2[entry2.count * 2];
1465
1466 ctr = 0;
1467 for(size_t i = 0; i < entry2.count; ++i) {
1468 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1469 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1470 }
1471
Ruben Brunk20796122015-07-21 17:51:54 -07001472 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count,
1473 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001474 }
1475 }
1476
1477 {
1478 // Set calibration transforms
1479 camera_metadata_entry entry1 =
1480 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk20796122015-07-21 17:51:54 -07001481 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001482
1483 int32_t calibrationTransform1[entry1.count * 2];
1484
1485 size_t ctr = 0;
1486 for(size_t i = 0; i < entry1.count; ++i) {
1487 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1488 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1489 }
1490
Ruben Brunk20796122015-07-21 17:51:54 -07001491 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001492 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001493
1494 if (!singleIlluminant) {
1495 camera_metadata_entry entry2 =
1496 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk20796122015-07-21 17:51:54 -07001497 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001498 int32_t calibrationTransform2[entry2.count * 2];
1499
1500 ctr = 0;
1501 for(size_t i = 0; i < entry2.count; ++i) {
1502 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1503 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1504 }
1505
Ruben Brunk20796122015-07-21 17:51:54 -07001506 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001507 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001508 }
1509 }
1510
1511 {
1512 // Set forward transforms
1513 camera_metadata_entry entry1 =
1514 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk20796122015-07-21 17:51:54 -07001515 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001516
1517 int32_t forwardTransform1[entry1.count * 2];
1518
1519 size_t ctr = 0;
1520 for(size_t i = 0; i < entry1.count; ++i) {
1521 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1522 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1523 }
1524
Ruben Brunk20796122015-07-21 17:51:54 -07001525 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count,
1526 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001527
1528 if (!singleIlluminant) {
1529 camera_metadata_entry entry2 =
1530 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk20796122015-07-21 17:51:54 -07001531 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001532 int32_t forwardTransform2[entry2.count * 2];
1533
1534 ctr = 0;
1535 for(size_t i = 0; i < entry2.count; ++i) {
1536 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1537 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1538 }
1539
Ruben Brunk20796122015-07-21 17:51:54 -07001540 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count,
1541 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001542 }
1543 }
1544
1545 {
1546 // Set camera neutral
1547 camera_metadata_entry entry =
1548 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk20796122015-07-21 17:51:54 -07001549 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001550 uint32_t cameraNeutral[entry.count * 2];
1551
1552 size_t ctr = 0;
1553 for(size_t i = 0; i < entry.count; ++i) {
1554 cameraNeutral[ctr++] =
1555 static_cast<uint32_t>(entry.data.r[i].numerator);
1556 cameraNeutral[ctr++] =
1557 static_cast<uint32_t>(entry.data.r[i].denominator);
1558 }
1559
Ruben Brunk20796122015-07-21 17:51:54 -07001560 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001561 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001562 }
1563
Ruben Brunkf967a542014-04-28 16:31:11 -07001564
1565 {
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001566 // Set dimensions
Ruben Brunk20796122015-07-21 17:51:54 -07001567 if (calculateAndSetCrop(env, characteristics, imageWidth, imageHeight, writer) != OK) {
1568 return nullptr;
1569 }
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001570 camera_metadata_entry entry =
1571 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
Ruben Brunk20796122015-07-21 17:51:54 -07001572 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07001573 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1574 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1575 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1576 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk20796122015-07-21 17:51:54 -07001577
1578 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width};
1579 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0),
1580 env, TAG_ACTIVEAREA, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001581 }
1582
1583 {
1584 // Setup unique camera model tag
1585 char model[PROPERTY_VALUE_MAX];
1586 property_get("ro.product.model", model, "");
1587
1588 char manufacturer[PROPERTY_VALUE_MAX];
1589 property_get("ro.product.manufacturer", manufacturer, "");
1590
1591 char brand[PROPERTY_VALUE_MAX];
1592 property_get("ro.product.brand", brand, "");
1593
1594 String8 cameraModel(model);
1595 cameraModel += "-";
1596 cameraModel += manufacturer;
1597 cameraModel += "-";
1598 cameraModel += brand;
1599
Ruben Brunk20796122015-07-21 17:51:54 -07001600 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
Ruben Brunkf967a542014-04-28 16:31:11 -07001601 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001602 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001603 }
1604
1605 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001606 // Setup sensor noise model
1607 camera_metadata_entry entry =
1608 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1609
Ruben Brunkd70132c2014-08-22 16:24:49 -07001610 const status_t numPlaneColors = 3;
1611 const status_t numCfaChannels = 4;
1612
1613 uint8_t cfaOut[numCfaChannels];
1614 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1615 jniThrowException(env, "java/lang/IllegalArgumentException",
1616 "Invalid CFA from camera characteristics");
Ruben Brunk20796122015-07-21 17:51:54 -07001617 return nullptr;
Ruben Brunkd70132c2014-08-22 16:24:49 -07001618 }
1619
1620 double noiseProfile[numPlaneColors * 2];
1621
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001622 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001623 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001624 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1625 "in characteristics, no noise profile tag written...",
1626 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001627 } else {
1628 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1629 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1630
Ruben Brunk20796122015-07-21 17:51:54 -07001631 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE,
1632 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE,
1633 writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001634 } else {
1635 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1636 " tag written...", __FUNCTION__);
1637 }
1638 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001639 } else {
1640 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1641 __FUNCTION__);
1642 }
1643 }
1644
1645 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001646 // Set up opcode List 2
1647 OpcodeListBuilder builder;
1648 status_t err = OK;
1649
1650 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001651 camera_metadata_entry entry1 =
1652 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001653
1654 uint32_t lsmWidth = 0;
1655 uint32_t lsmHeight = 0;
1656
1657 if (entry1.count != 0) {
1658 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1659 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1660 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001661
1662 camera_metadata_entry entry2 =
1663 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001664
Ruben Brunk20796122015-07-21 17:51:54 -07001665 camera_metadata_entry entry =
1666 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
1667 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer);
1668 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]);
1669 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]);
1670 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
1671 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001672 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001673 err = builder.addGainMapsForMetadata(lsmWidth,
1674 lsmHeight,
Ruben Brunk20796122015-07-21 17:51:54 -07001675 ymin,
1676 xmin,
1677 height,
1678 width,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001679 opcodeCfaLayout,
1680 entry2.data.f);
1681 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001682 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1683 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk20796122015-07-21 17:51:54 -07001684 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001685 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001686 }
1687
Ruben Brunkfe816622015-06-16 23:03:09 -07001688 size_t listSize = builder.getSize();
1689 uint8_t opcodeListBuf[listSize];
1690 err = builder.buildOpList(opcodeListBuf);
1691 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001692 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001693 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1694 } else {
1695 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1696 "map.", __FUNCTION__);
1697 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1698 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001699 return nullptr;
Ruben Brunkfe816622015-06-16 23:03:09 -07001700 }
1701 }
1702
1703 {
1704 // Set up opcode List 3
1705 OpcodeListBuilder builder;
1706 status_t err = OK;
1707
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001708 // Set up rectilinear distortion correction
1709 camera_metadata_entry entry3 =
1710 results.find(ANDROID_LENS_RADIAL_DISTORTION);
1711 camera_metadata_entry entry4 =
1712 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1713
1714 if (entry3.count == 6 && entry4.count == 5) {
1715 float cx = entry4.data.f[/*c_x*/2];
1716 float cy = entry4.data.f[/*c_y*/3];
Ruben Brunk20796122015-07-21 17:51:54 -07001717 err = builder.addWarpRectilinearForMetadata(entry3.data.f, preWidth, preHeight, cx,
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001718 cy);
1719 if (err != OK) {
1720 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1721 jniThrowRuntimeException(env, "failed to add distortion correction.");
Ruben Brunk20796122015-07-21 17:51:54 -07001722 return nullptr;
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001723 }
1724 }
1725
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001726 size_t listSize = builder.getSize();
1727 uint8_t opcodeListBuf[listSize];
1728 err = builder.buildOpList(opcodeListBuf);
1729 if (err == OK) {
Ruben Brunk20796122015-07-21 17:51:54 -07001730 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
Ruben Brunkfe816622015-06-16 23:03:09 -07001731 TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001732 } else {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001733 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1734 "map.", __FUNCTION__);
1735 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1736 " correction and lens shading map");
Ruben Brunk20796122015-07-21 17:51:54 -07001737 return nullptr;
Ruben Brunkf967a542014-04-28 16:31:11 -07001738 }
1739 }
1740
Ruben Brunk20796122015-07-21 17:51:54 -07001741 {
1742 // Set up orientation tags.
1743 uint16_t orientation = nativeContext->getOrientation();
1744 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
1745 env, TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001746
Ruben Brunk47e91f22014-05-28 18:38:42 -07001747 }
1748
Ruben Brunk20796122015-07-21 17:51:54 -07001749 if (nativeContext->hasDescription()){
1750 // Set Description
1751 String8 description = nativeContext->getDescription();
1752 size_t len = description.bytes() + 1;
1753 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1754 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
1755 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1756 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001757 }
1758 }
1759
Ruben Brunk20796122015-07-21 17:51:54 -07001760 if (nativeContext->hasGpsData()) {
1761 // Set GPS tags
1762 GpsData gpsData = nativeContext->getGpsData();
1763 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1764 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1765 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1766 TIFF_IFD_0);
1767 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1768 return nullptr;
1769 }
1770 }
1771
1772 {
1773 uint8_t version[] = {2, 3, 0, 0};
1774 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1775 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1776 }
1777
1778 {
1779 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF,
1780 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env,
1781 TAG_GPSLATITUDEREF, writer);
1782 }
1783
1784 {
1785 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF,
1786 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env,
1787 TAG_GPSLONGITUDEREF, writer);
1788 }
1789
1790 {
1791 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude,
1792 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1793 }
1794
1795 {
1796 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude,
1797 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1798 }
1799
1800 {
1801 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp,
1802 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1803 }
1804
1805 {
1806 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP,
1807 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env,
1808 TAG_GPSDATESTAMP, writer);
1809 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001810 }
1811
Ruben Brunk47e91f22014-05-28 18:38:42 -07001812
Ruben Brunk20796122015-07-21 17:51:54 -07001813 if (nativeContext->hasThumbnail()) {
1814 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1815 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1816 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1817 TIFF_IFD_0);
1818 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1819 return nullptr;
1820 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001821 }
1822
1823 Vector<uint16_t> tagsToMove;
1824 tagsToMove.add(TAG_ORIENTATION);
1825 tagsToMove.add(TAG_NEWSUBFILETYPE);
Ruben Brunk20796122015-07-21 17:51:54 -07001826 tagsToMove.add(TAG_ACTIVEAREA);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001827 tagsToMove.add(TAG_BITSPERSAMPLE);
1828 tagsToMove.add(TAG_COMPRESSION);
1829 tagsToMove.add(TAG_IMAGEWIDTH);
1830 tagsToMove.add(TAG_IMAGELENGTH);
1831 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1832 tagsToMove.add(TAG_BLACKLEVEL);
1833 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1834 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1835 tagsToMove.add(TAG_PLANARCONFIGURATION);
1836 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1837 tagsToMove.add(TAG_CFAPATTERN);
1838 tagsToMove.add(TAG_CFAPLANECOLOR);
1839 tagsToMove.add(TAG_CFALAYOUT);
1840 tagsToMove.add(TAG_XRESOLUTION);
1841 tagsToMove.add(TAG_YRESOLUTION);
1842 tagsToMove.add(TAG_RESOLUTIONUNIT);
1843 tagsToMove.add(TAG_WHITELEVEL);
1844 tagsToMove.add(TAG_DEFAULTSCALE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001845 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1846 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1847 tagsToMove.add(TAG_OPCODELIST2);
Ruben Brunkfe816622015-06-16 23:03:09 -07001848 tagsToMove.add(TAG_OPCODELIST3);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001849
1850 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1851 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
Ruben Brunk20796122015-07-21 17:51:54 -07001852 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001853 }
1854
1855 // Make sure both IFDs get the same orientation tag
1856 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
Ruben Brunk20796122015-07-21 17:51:54 -07001857 if (orientEntry.get() != nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001858 writer->addEntry(orientEntry, TIFF_IFD_0);
1859 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001860
Ruben Brunk20796122015-07-21 17:51:54 -07001861 // Setup thumbnail tags
Ruben Brunk47e91f22014-05-28 18:38:42 -07001862
Ruben Brunk20796122015-07-21 17:51:54 -07001863 {
1864 // Set photometric interpretation
1865 uint16_t interpretation = 2; // RGB
1866 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
1867 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001868 }
Ruben Brunk20796122015-07-21 17:51:54 -07001869
1870 {
1871 // Set planar configuration
1872 uint16_t config = 1; // Chunky
1873 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
1874 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
1875 }
1876
1877 {
1878 // Set samples per pixel
1879 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1880 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
1881 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
1882 }
1883
1884 {
1885 // Set bits per sample
1886 uint16_t bits = BITS_PER_RGB_SAMPLE;
1887 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0),
1888 env, TAG_BITSPERSAMPLE, writer);
1889 }
1890
1891 {
1892 // Set subfiletype
1893 uint32_t subfileType = 1; // Thumbnail image
1894 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
1895 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
1896 }
1897
1898 {
1899 // Set compression
1900 uint16_t compression = 1; // None
1901 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
1902 TIFF_IFD_0), env, TAG_COMPRESSION, writer);
1903 }
1904
1905 {
1906 // Set dimensions
1907 uint32_t uWidth = nativeContext->getThumbnailWidth();
1908 uint32_t uHeight = nativeContext->getThumbnailHeight();
1909 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
1910 env, TAG_IMAGEWIDTH, writer);
1911 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
1912 env, TAG_IMAGELENGTH, writer);
1913 }
1914
1915 {
1916 // x resolution
1917 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1918 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1919 env, TAG_XRESOLUTION, writer);
1920
1921 // y resolution
1922 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1923 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1924 env, TAG_YRESOLUTION, writer);
1925
1926 uint16_t unit = 2; // inches
1927 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1928 env, TAG_RESOLUTIONUNIT, writer);
1929 }
1930 }
1931
1932 if (writer->addStrip(TIFF_IFD_0) != OK) {
1933 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1934 jniThrowException(env, "java/lang/IllegalStateException",
1935 "Failed to setup thumbnail strip tags.");
1936 return nullptr;
1937 }
1938
1939 if (writer->hasIfd(TIFF_IFD_SUB1)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001940 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1941 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1942 jniThrowException(env, "java/lang/IllegalStateException",
1943 "Failed to setup main image strip tags.");
Ruben Brunk20796122015-07-21 17:51:54 -07001944 return nullptr;
Ruben Brunk47e91f22014-05-28 18:38:42 -07001945 }
1946 }
Ruben Brunk20796122015-07-21 17:51:54 -07001947 return writer;
1948}
1949
1950static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1951 ALOGV("%s:", __FUNCTION__);
1952 DngCreator_setNativeContext(env, thiz, nullptr);
1953}
1954
1955static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
1956 ALOGV("%s:", __FUNCTION__);
1957
1958 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1959 if (context == nullptr) {
1960 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1961 jniThrowException(env, "java/lang/AssertionError",
1962 "setOrientation called with uninitialized DngCreator");
1963 return;
1964 }
1965
1966 uint16_t orientation = static_cast<uint16_t>(orient);
1967 context->setOrientation(orientation);
1968}
1969
1970static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
1971 ALOGV("%s:", __FUNCTION__);
1972
1973 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1974 if (context == nullptr) {
1975 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1976 jniThrowException(env, "java/lang/AssertionError",
1977 "setDescription called with uninitialized DngCreator");
1978 return;
1979 }
1980
1981 const char* desc = env->GetStringUTFChars(description, nullptr);
1982 context->setDescription(String8(desc));
1983 env->ReleaseStringUTFChars(description, desc);
1984}
1985
1986static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag,
1987 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
1988 ALOGV("%s:", __FUNCTION__);
1989
1990 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1991 if (context == nullptr) {
1992 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1993 jniThrowException(env, "java/lang/AssertionError",
1994 "setGpsTags called with uninitialized DngCreator");
1995 return;
1996 }
1997
1998 GpsData data;
1999
2000 jsize latLen = env->GetArrayLength(latTag);
2001 jsize longLen = env->GetArrayLength(longTag);
2002 jsize timeLen = env->GetArrayLength(timeTag);
2003 if (latLen != GpsData::GPS_VALUE_LENGTH) {
2004 jniThrowException(env, "java/lang/IllegalArgumentException",
2005 "invalid latitude tag length");
2006 return;
2007 } else if (longLen != GpsData::GPS_VALUE_LENGTH) {
2008 jniThrowException(env, "java/lang/IllegalArgumentException",
2009 "invalid longitude tag length");
2010 return;
2011 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) {
2012 jniThrowException(env, "java/lang/IllegalArgumentException",
2013 "invalid time tag length");
2014 return;
2015 }
2016
2017 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2018 reinterpret_cast<jint*>(&data.mLatitude));
2019 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2020 reinterpret_cast<jint*>(&data.mLongitude));
2021 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH),
2022 reinterpret_cast<jint*>(&data.mTimestamp));
2023
2024
2025 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef));
2026 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2027 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef));
2028 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0';
2029 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1,
2030 reinterpret_cast<char*>(&data.mDate));
2031 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0';
2032
2033 context->setGpsData(data);
2034}
2035
2036static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
2037 jint height) {
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 "setThumbnail called with uninitialized DngCreator");
2045 return;
2046 }
2047
2048 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
2049 jlong capacity = env->GetDirectBufferCapacity(buffer);
2050 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
2051 jniThrowExceptionFmt(env, "java/lang/AssertionError",
2052 "Invalid size %d for thumbnail, expected size was %d",
2053 capacity, fullSize);
2054 return;
2055 }
2056
2057 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
2058 if (pixelBytes == nullptr) {
2059 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2060 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2061 return;
2062 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002063
2064 if (!context->setThumbnail(pixelBytes, width, height)) {
2065 jniThrowException(env, "java/lang/IllegalStateException",
2066 "Failed to set thumbnail.");
2067 return;
2068 }
2069}
2070
2071// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07002072static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002073 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
2074 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002075 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08002076 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
2077 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2078 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002079 uint32_t rStride = static_cast<uint32_t>(rowStride);
2080 uint32_t pStride = static_cast<uint32_t>(pixStride);
2081 uint32_t uWidth = static_cast<uint32_t>(width);
2082 uint32_t uHeight = static_cast<uint32_t>(height);
2083 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07002084
2085 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
2086 if(env->ExceptionCheck()) {
2087 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2088 return;
2089 }
2090
Ruben Brunk47e91f22014-05-28 18:38:42 -07002091 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002092 if (context == nullptr) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002093 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2094 jniThrowException(env, "java/lang/AssertionError",
2095 "Write called with uninitialized DngCreator");
2096 return;
2097 }
Ruben Brunk20796122015-07-21 17:51:54 -07002098 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002099
Ruben Brunk20796122015-07-21 17:51:54 -07002100 if (writer.get() == nullptr) {
2101 return;
2102 }
2103
2104 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002105 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002106 return;
2107 }
2108
Ruben Brunk47e91f22014-05-28 18:38:42 -07002109 sp<JniInputByteBuffer> inBuf;
2110 Vector<StripSource*> sources;
2111 sp<DirectStripSource> thumbnailSource;
2112 uint32_t targetIfd = TIFF_IFD_0;
2113
2114 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2115
2116 if (hasThumbnail) {
2117 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2118 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2119 uint32_t thumbWidth = context->getThumbnailWidth();
2120 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2121 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
2122 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2123 SAMPLES_PER_RGB_PIXEL);
2124 sources.add(thumbnailSource.get());
2125 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07002126 }
2127
Ruben Brunk47e91f22014-05-28 18:38:42 -07002128 if (isDirect) {
2129 size_t fullSize = rStride * uHeight;
2130 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
2131 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
2132 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
2133 "Invalid size %d for Image, size given in metadata is %d at current stride",
2134 capacity, fullSize);
2135 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002136 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002137
Ruben Brunk47e91f22014-05-28 18:38:42 -07002138 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
Ruben Brunk20796122015-07-21 17:51:54 -07002139 if (pixelBytes == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002140 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
2141 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
2142 return;
2143 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002144
Ruben Brunk47e91f22014-05-28 18:38:42 -07002145 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
2146 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
2147 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2148 sources.add(&stripSource);
2149
2150 status_t ret = OK;
2151 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2152 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002153 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002154 jniThrowExceptionFmt(env, "java/io/IOException",
2155 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002156 }
2157 return;
2158 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002159 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002160 inBuf = new JniInputByteBuffer(env, inBuffer);
2161
2162 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2163 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
2164 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2165 sources.add(&stripSource);
2166
2167 status_t ret = OK;
2168 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2169 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2170 if (!env->ExceptionCheck()) {
2171 jniThrowExceptionFmt(env, "java/io/IOException",
2172 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07002173 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07002174 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07002175 }
2176 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002177}
2178
2179static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07002180 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07002181 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002182
2183 uint32_t rowStride = width * BYTES_PER_SAMPLE;
2184 uint32_t pixStride = BYTES_PER_SAMPLE;
2185 uint32_t uWidth = static_cast<uint32_t>(width);
2186 uint32_t uHeight = static_cast<uint32_t>(height);
2187 uint64_t uOffset = static_cast<uint32_t>(offset);
2188
Dan Albert46d84442014-11-18 16:07:51 -08002189 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
2190 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
2191 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002192
2193 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08002194 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002195 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
2196 return;
2197 }
2198
Ruben Brunk47e91f22014-05-28 18:38:42 -07002199 NativeContext* context = DngCreator_getNativeContext(env, thiz);
Ruben Brunk20796122015-07-21 17:51:54 -07002200 if (context == nullptr) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002201 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
2202 jniThrowException(env, "java/lang/AssertionError",
2203 "Write called with uninitialized DngCreator");
2204 return;
2205 }
Ruben Brunk20796122015-07-21 17:51:54 -07002206 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight);
Ruben Brunk47e91f22014-05-28 18:38:42 -07002207
Ruben Brunk20796122015-07-21 17:51:54 -07002208 if (writer.get() == nullptr) {
2209 return;
2210 }
2211
2212 // Validate DNG size
Ruben Brunkb8f4c6a2015-06-22 19:17:30 -07002213 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07002214 return;
2215 }
2216
2217 sp<DirectStripSource> thumbnailSource;
2218 uint32_t targetIfd = TIFF_IFD_0;
2219 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
2220 Vector<StripSource*> sources;
2221
2222 if (hasThumbnail) {
2223 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
2224 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
2225 uint32_t width = context->getThumbnailWidth();
2226 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
2227 width, context->getThumbnailHeight(), bytesPerPixel,
2228 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
2229 SAMPLES_PER_RGB_PIXEL);
2230 sources.add(thumbnailSource.get());
2231 targetIfd = TIFF_IFD_SUB1;
2232 }
2233
2234 sp<JniInputStream> in = new JniInputStream(env, inStream);
2235
2236 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2237 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2238 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2239 sources.add(&stripSource);
2240
2241 status_t ret = OK;
2242 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2243 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2244 if (!env->ExceptionCheck()) {
2245 jniThrowExceptionFmt(env, "java/io/IOException",
2246 "Encountered error %d while writing file.", ret);
2247 }
2248 return;
2249 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002250}
2251
2252} /*extern "C" */
2253
2254static JNINativeMethod gDngCreatorMethods[] = {
2255 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2256 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002257 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2258 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002259 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2260 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002261 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2262 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2263 (void*) DngCreator_nativeSetGpsTags},
2264 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2265 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002266 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002267 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002268 (void*) DngCreator_nativeWriteInputStream},
2269};
2270
Ruben Brunkb6079002014-05-22 12:33:54 -07002271int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002272 return RegisterMethodsOrDie(env,
2273 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002274}