blob: 31c7b9f336588a59a78a5bd72ba3d4a25a8d9d76 [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
17//#define LOG_NDEBUG 0
18#define LOG_TAG "DngCreator_JNI"
19
20#include <system/camera_metadata.h>
21#include <camera/CameraMetadata.h>
22#include <img_utils/DngUtils.h>
23#include <img_utils/TagDefinitions.h>
24#include <img_utils/TiffIfd.h>
25#include <img_utils/TiffWriter.h>
26#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070027#include <img_utils/Input.h>
28#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070029
30#include <utils/Log.h>
31#include <utils/Errors.h>
32#include <utils/StrongPointer.h>
33#include <utils/RefBase.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070034#include <utils/Vector.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070035#include <cutils/properties.h>
36
Ruben Brunkb8df8e02014-06-02 22:59:45 -070037#include <string.h>
38
Ruben Brunkf967a542014-04-28 16:31:11 -070039#include "android_runtime/AndroidRuntime.h"
40#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
41
42#include <jni.h>
43#include <JNIHelp.h>
44
45using namespace android;
46using namespace img_utils;
47
Ruben Brunk47e91f22014-05-28 18:38:42 -070048#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070049 if ((expr) != OK) { \
50 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070051 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070052 return; \
53 }
54
Ruben Brunk47e91f22014-05-28 18:38:42 -070055#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070056 if (entry.count == 0) { \
57 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070058 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070059 return; \
60 }
61
Ruben Brunkb6079002014-05-22 12:33:54 -070062#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070063
64static struct {
65 jfieldID mNativeContext;
66} gDngCreatorClassInfo;
67
68static struct {
69 jmethodID mWriteMethod;
70} gOutputStreamClassInfo;
71
Ruben Brunk47e91f22014-05-28 18:38:42 -070072static struct {
73 jmethodID mReadMethod;
74 jmethodID mSkipMethod;
75} gInputStreamClassInfo;
76
77static struct {
78 jmethodID mGetMethod;
79} gInputByteBufferClassInfo;
80
Ruben Brunkf967a542014-04-28 16:31:11 -070081enum {
82 BITS_PER_SAMPLE = 16,
83 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -070084 BYTES_PER_RGB_PIXEL = 3,
85 BITS_PER_RGB_SAMPLE = 8,
86 BYTES_PER_RGB_SAMPLE = 1,
87 SAMPLES_PER_RGB_PIXEL = 3,
88 SAMPLES_PER_RAW_PIXEL = 1,
89 TIFF_IFD_0 = 0,
90 TIFF_IFD_SUB1 = 1,
91 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -070092};
93
94// ----------------------------------------------------------------------------
95
Ruben Brunk47e91f22014-05-28 18:38:42 -070096/**
97 * Container class for the persistent native context.
98 */
99
100class NativeContext : public LightRefBase<NativeContext> {
101
102public:
103 NativeContext();
104 virtual ~NativeContext();
105
106 TiffWriter* getWriter();
107
108 uint32_t getThumbnailWidth();
109 uint32_t getThumbnailHeight();
110 const uint8_t* getThumbnail();
111
112 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
113
114private:
115 Vector<uint8_t> mCurrentThumbnail;
116 TiffWriter mWriter;
117 uint32_t mThumbnailWidth;
118 uint32_t mThumbnailHeight;
119};
120
121NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
122
123NativeContext::~NativeContext() {}
124
125TiffWriter* NativeContext::getWriter() {
126 return &mWriter;
127}
128
129uint32_t NativeContext::getThumbnailWidth() {
130 return mThumbnailWidth;
131}
132
133uint32_t NativeContext::getThumbnailHeight() {
134 return mThumbnailHeight;
135}
136
137const uint8_t* NativeContext::getThumbnail() {
138 return mCurrentThumbnail.array();
139}
140
141bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
142 mThumbnailWidth = width;
143 mThumbnailHeight = height;
144
145 size_t size = BYTES_PER_RGB_PIXEL * width * height;
146 if (mCurrentThumbnail.resize(size) < 0) {
147 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
148 return false;
149 }
150
151 uint8_t* thumb = mCurrentThumbnail.editArray();
152 memcpy(thumb, buffer, size);
153 return true;
154}
155
156// End of NativeContext
157// ----------------------------------------------------------------------------
158
159/**
160 * Wrapper class for a Java OutputStream.
161 *
162 * This class is not intended to be used across JNI calls.
163 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700164class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
165public:
166 JniOutputStream(JNIEnv* env, jobject outStream);
167
168 virtual ~JniOutputStream();
169
170 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700171
Ruben Brunkf967a542014-04-28 16:31:11 -0700172 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700173
Ruben Brunkf967a542014-04-28 16:31:11 -0700174 status_t close();
175private:
176 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700177 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700178 };
179 jobject mOutputStream;
180 JNIEnv* mEnv;
181 jbyteArray mByteArray;
182};
183
184JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
185 mEnv(env) {
186 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
187 if (mByteArray == NULL) {
188 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
189 }
190}
191
192JniOutputStream::~JniOutputStream() {
193 mEnv->DeleteLocalRef(mByteArray);
194}
195
196status_t JniOutputStream::open() {
197 // Do nothing
198 return OK;
199}
200
201status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
202 while(count > 0) {
203 size_t len = BYTE_ARRAY_LENGTH;
204 len = (count > len) ? len : count;
205 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
206
207 if (mEnv->ExceptionCheck()) {
208 return BAD_VALUE;
209 }
210
211 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
212 0, len);
213
214 if (mEnv->ExceptionCheck()) {
215 return BAD_VALUE;
216 }
217
218 count -= len;
219 offset += len;
220 }
221 return OK;
222}
223
224status_t JniOutputStream::close() {
225 // Do nothing
226 return OK;
227}
228
Ruben Brunk47e91f22014-05-28 18:38:42 -0700229// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700230// ----------------------------------------------------------------------------
231
Ruben Brunk47e91f22014-05-28 18:38:42 -0700232/**
233 * Wrapper class for a Java InputStream.
234 *
235 * This class is not intended to be used across JNI calls.
236 */
237class JniInputStream : public Input, public LightRefBase<JniInputStream> {
238public:
239 JniInputStream(JNIEnv* env, jobject inStream);
240
241 status_t open();
242
243 status_t close();
244
245 ssize_t read(uint8_t* buf, size_t offset, size_t count);
246
247 ssize_t skip(size_t count);
248
249 virtual ~JniInputStream();
250private:
251 enum {
252 BYTE_ARRAY_LENGTH = 4096
253 };
254 jobject mInStream;
255 JNIEnv* mEnv;
256 jbyteArray mByteArray;
257
258};
259
260JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
261 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
262 if (mByteArray == NULL) {
263 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
264 }
265}
266
267JniInputStream::~JniInputStream() {
268 mEnv->DeleteLocalRef(mByteArray);
269}
270
271ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
272
273 jint realCount = BYTE_ARRAY_LENGTH;
274 if (count < BYTE_ARRAY_LENGTH) {
275 realCount = count;
276 }
277 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
278 realCount);
279
280 if (actual < 0) {
281 return NOT_ENOUGH_DATA;
282 }
283
284 if (mEnv->ExceptionCheck()) {
285 return BAD_VALUE;
286 }
287
288 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
289 if (mEnv->ExceptionCheck()) {
290 return BAD_VALUE;
291 }
292 return actual;
293}
294
295ssize_t JniInputStream::skip(size_t count) {
296 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
297 static_cast<jlong>(count));
298
299 if (mEnv->ExceptionCheck()) {
300 return BAD_VALUE;
301 }
302 if (actual < 0) {
303 return NOT_ENOUGH_DATA;
304 }
305 return actual;
306}
307
308status_t JniInputStream::open() {
309 // Do nothing
310 return OK;
311}
312
313status_t JniInputStream::close() {
314 // Do nothing
315 return OK;
316}
317
318// End of JniInputStream
319// ----------------------------------------------------------------------------
320
321/**
322 * Wrapper class for a non-direct Java ByteBuffer.
323 *
324 * This class is not intended to be used across JNI calls.
325 */
326class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
327public:
328 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
329
330 status_t open();
331
332 status_t close();
333
334 ssize_t read(uint8_t* buf, size_t offset, size_t count);
335
336 virtual ~JniInputByteBuffer();
337private:
338 enum {
339 BYTE_ARRAY_LENGTH = 4096
340 };
341 jobject mInBuf;
342 JNIEnv* mEnv;
343 jbyteArray mByteArray;
344};
345
346JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
347 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
348 if (mByteArray == NULL) {
349 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
350 }
351}
352
353JniInputByteBuffer::~JniInputByteBuffer() {
354 mEnv->DeleteLocalRef(mByteArray);
355}
356
357ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
358 jint realCount = BYTE_ARRAY_LENGTH;
359 if (count < BYTE_ARRAY_LENGTH) {
360 realCount = count;
361 }
362
363 mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
364 realCount);
365
366 if (mEnv->ExceptionCheck()) {
367 return BAD_VALUE;
368 }
369
370 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
371 if (mEnv->ExceptionCheck()) {
372 return BAD_VALUE;
373 }
374 return realCount;
375}
376
377status_t JniInputByteBuffer::open() {
378 // Do nothing
379 return OK;
380}
381
382status_t JniInputByteBuffer::close() {
383 // Do nothing
384 return OK;
385}
386
387// End of JniInputByteBuffer
388// ----------------------------------------------------------------------------
389
390/**
391 * StripSource subclass for Input types.
392 *
393 * This class is not intended to be used across JNI calls.
394 */
395
396class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
397public:
398 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
399 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
400 uint32_t samplesPerPixel);
401
402 virtual ~InputStripSource();
403
404 virtual status_t writeToStream(Output& stream, uint32_t count);
405
406 virtual uint32_t getIfd() const;
407protected:
408 uint32_t mIfd;
409 Input* mInput;
410 uint32_t mWidth;
411 uint32_t mHeight;
412 uint32_t mPixStride;
413 uint32_t mRowStride;
414 uint64_t mOffset;
415 JNIEnv* mEnv;
416 uint32_t mBytesPerSample;
417 uint32_t mSamplesPerPixel;
418};
419
420InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
421 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
422 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
423 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
424 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
425 mSamplesPerPixel(samplesPerPixel) {}
426
427InputStripSource::~InputStripSource() {}
428
429status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
430 status_t err = OK;
Ruben Brunk3e190252014-08-12 11:09:01 -0700431 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700432 jlong offset = mOffset;
433
434 if (fullSize != count) {
435 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
436 fullSize);
437 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
438 return BAD_VALUE;
439 }
440
441 // Skip offset
442 while (offset > 0) {
443 ssize_t skipped = mInput->skip(offset);
444 if (skipped <= 0) {
445 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
446 jniThrowExceptionFmt(mEnv, "java/io/IOException",
447 "Early EOF encountered in skip, not enough pixel data for image of size %u",
448 fullSize);
449 skipped = NOT_ENOUGH_DATA;
450 } else {
451 if (!mEnv->ExceptionCheck()) {
452 jniThrowException(mEnv, "java/io/IOException",
453 "Error encountered while skip bytes in input stream.");
454 }
455 }
456
457 return skipped;
458 }
459 offset -= skipped;
460 }
461
462 Vector<uint8_t> row;
463 if (row.resize(mRowStride) < 0) {
464 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
465 return BAD_VALUE;
466 }
467
468 uint8_t* rowBytes = row.editArray();
469
470 for (uint32_t i = 0; i < mHeight; ++i) {
471 size_t rowFillAmt = 0;
472 size_t rowSize = mPixStride;
473
474 while (rowFillAmt < mRowStride) {
475 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
476 if (bytesRead <= 0) {
477 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
478 jniThrowExceptionFmt(mEnv, "java/io/IOException",
479 "Early EOF encountered, not enough pixel data for image of size %u",
480 fullSize);
481 bytesRead = NOT_ENOUGH_DATA;
482 } else {
483 if (!mEnv->ExceptionCheck()) {
484 jniThrowException(mEnv, "java/io/IOException",
485 "Error encountered while reading");
486 }
487 }
488 return bytesRead;
489 }
490 rowFillAmt += bytesRead;
491 rowSize -= bytesRead;
492 }
493
494 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
495 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
496
497 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
498 mEnv->ExceptionCheck()) {
499 if (!mEnv->ExceptionCheck()) {
500 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
501 }
502 return BAD_VALUE;
503 }
504 } else {
505 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
506 jniThrowException(mEnv, "java/lang/IllegalStateException",
507 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
508 return BAD_VALUE;
509
510 // TODO: Add support for non-contiguous pixels if needed.
511 }
512 }
513 return OK;
514}
515
516uint32_t InputStripSource::getIfd() const {
517 return mIfd;
518}
519
520// End of InputStripSource
521// ----------------------------------------------------------------------------
522
523/**
524 * StripSource subclass for direct buffer types.
525 *
526 * This class is not intended to be used across JNI calls.
527 */
528
529class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
530public:
531 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
532 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
533 uint32_t bytesPerSample, uint32_t samplesPerPixel);
534
535 virtual ~DirectStripSource();
536
537 virtual status_t writeToStream(Output& stream, uint32_t count);
538
539 virtual uint32_t getIfd() const;
540protected:
541 uint32_t mIfd;
542 const uint8_t* mPixelBytes;
543 uint32_t mWidth;
544 uint32_t mHeight;
545 uint32_t mPixStride;
546 uint32_t mRowStride;
547 uint16_t mOffset;
548 JNIEnv* mEnv;
549 uint32_t mBytesPerSample;
550 uint32_t mSamplesPerPixel;
551};
552
553DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
554 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
555 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
556 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
557 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
558 mSamplesPerPixel(samplesPerPixel) {}
559
560DirectStripSource::~DirectStripSource() {}
561
562status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700563 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700564
565 if (fullSize != count) {
566 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
567 fullSize);
568 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
569 return BAD_VALUE;
570 }
571
572 if (mPixStride == mBytesPerSample * mSamplesPerPixel
573 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
574 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
575
576 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
577 if (!mEnv->ExceptionCheck()) {
578 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
579 }
580 return BAD_VALUE;
581 }
582 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
583 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
584
585 for (size_t i = 0; i < mHeight; ++i) {
586 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
587 mEnv->ExceptionCheck()) {
588 if (!mEnv->ExceptionCheck()) {
589 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
590 }
591 return BAD_VALUE;
592 }
593 }
594 } else {
595 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
596
597 jniThrowException(mEnv, "java/lang/IllegalStateException",
598 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
599 return BAD_VALUE;
600
601 // TODO: Add support for non-contiguous pixels if needed.
602 }
603 return OK;
604
605}
606
607uint32_t DirectStripSource::getIfd() const {
608 return mIfd;
609}
610
611// End of DirectStripSource
612// ----------------------------------------------------------------------------
613
614static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
615 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
616
617 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
618 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
619 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
620
621 if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
622 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
623 "Metadata width %d doesn't match image width %d", metadataWidth, width);
624 return false;
625 }
626
627 if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
628 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
629 "Metadata height %d doesn't match image height %d", metadataHeight, height);
630 return false;
631 }
632
633 return true;
634}
635
636static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
637 const Vector<uint16_t>& entries) {
638 for (size_t i = 0; i < entries.size(); ++i) {
639 uint16_t tagId = entries[i];
640 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
641 if (entry == NULL) {
642 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
643 ifdFrom);
644 return BAD_VALUE;
645 }
646 if (writer->addEntry(entry, ifdTo) != OK) {
647 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
648 ifdFrom);
649 return BAD_VALUE;
650 }
651 writer->removeEntry(tagId, ifdFrom);
652 }
653 return OK;
654}
655
Ruben Brunkd70132c2014-08-22 16:24:49 -0700656/**
657 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
658 * Returns OK on success, or a negative error code if the CFA enum was invalid.
659 */
660static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
661 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
662 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
663 cfaEnum);
664 switch(cfa) {
665 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
666 cfaOut[0] = 0;
667 cfaOut[1] = 1;
668 cfaOut[2] = 1;
669 cfaOut[3] = 2;
670 break;
671 }
672 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
673 cfaOut[0] = 1;
674 cfaOut[1] = 0;
675 cfaOut[2] = 2;
676 cfaOut[3] = 1;
677 break;
678 }
679 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
680 cfaOut[0] = 1;
681 cfaOut[1] = 2;
682 cfaOut[2] = 0;
683 cfaOut[3] = 1;
684 break;
685 }
686 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
687 cfaOut[0] = 2;
688 cfaOut[1] = 1;
689 cfaOut[2] = 1;
690 cfaOut[3] = 0;
691 break;
692 }
693 default: {
694 return BAD_VALUE;
695 }
696 }
697 return OK;
698}
699
700/**
701 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
702 * RGGB for an unknown enum.
703 */
704static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
705 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
706 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
707 cfaEnum);
708 switch(cfa) {
709 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
710 return OpcodeListBuilder::CFA_RGGB;
711 }
712 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
713 return OpcodeListBuilder::CFA_GRBG;
714 }
715 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
716 return OpcodeListBuilder::CFA_GBRG;
717 }
718 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
719 return OpcodeListBuilder::CFA_BGGR;
720 }
721 default: {
722 return OpcodeListBuilder::CFA_RGGB;
723 }
724 }
725}
726
727/**
728 * For each color plane, find the corresponding noise profile coefficients given in the
729 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
730 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
731 *
732 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
733 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
734 * coefficients.
735 * numChannels - the number of noise profile coefficient pairs and color channels given in
736 * the perChannelNoiseProfile and cfa arguments, respectively.
737 * planeColors - the color planes in the noise profile output.
738 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
739 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
740 *
741 * returns OK, or a negative error code on failure.
742 */
743static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
744 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
745 /*out*/double* noiseProfile) {
746
747 for (size_t p = 0; p < numPlanes; ++p) {
748 size_t S = p * 2;
749 size_t O = p * 2 + 1;
750
751 noiseProfile[S] = 0;
752 noiseProfile[O] = 0;
753 bool uninitialized = true;
754 for (size_t c = 0; c < numChannels; ++c) {
755 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
756 noiseProfile[S] = perChannelNoiseProfile[c * 2];
757 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
758 uninitialized = false;
759 }
760 }
761 if (uninitialized) {
762 ALOGE("%s: No valid NoiseProfile coefficients for color plane %u", __FUNCTION__, p);
763 return BAD_VALUE;
764 }
765 }
766 return OK;
767}
768
Ruben Brunk47e91f22014-05-28 18:38:42 -0700769// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700770extern "C" {
771
Ruben Brunk47e91f22014-05-28 18:38:42 -0700772static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700773 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700774 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700775 gDngCreatorClassInfo.mNativeContext));
776}
777
Ruben Brunk47e91f22014-05-28 18:38:42 -0700778static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700779 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700780 NativeContext* current = DngCreator_getNativeContext(env, thiz);
781
782 if (context != NULL) {
783 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700784 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700785
Ruben Brunkf967a542014-04-28 16:31:11 -0700786 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700787 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700788 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700789
Ruben Brunkf967a542014-04-28 16:31:11 -0700790 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700791 reinterpret_cast<jlong>(context.get()));
792}
793
794static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
795 ALOGV("%s:", __FUNCTION__);
796 NativeContext* current = DngCreator_getNativeContext(env, thiz);
797 if (current) {
798 return current->getWriter();
799 }
800 return NULL;
Ruben Brunkf967a542014-04-28 16:31:11 -0700801}
802
803static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
804 ALOGV("%s:", __FUNCTION__);
805
806 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
Ruben Brunkb6079002014-05-22 12:33:54 -0700807 ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700808 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
Ruben Brunkb6079002014-05-22 12:33:54 -0700809 "can't find android/hardware/camera2/DngCreator.%s",
810 ANDROID_DNGCREATOR_CTX_JNI_ID);
Ruben Brunkf967a542014-04-28 16:31:11 -0700811
812 jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
813 LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
814 gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
815 LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700816
817 jclass inputStreamClazz = env->FindClass("java/io/InputStream");
818 LOG_ALWAYS_FATAL_IF(inputStreamClazz == NULL, "Can't find java/io/InputStream class");
819 gInputStreamClassInfo.mReadMethod = env->GetMethodID(inputStreamClazz, "read", "([BII)I");
820 LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mReadMethod == NULL, "Can't find read method");
821 gInputStreamClassInfo.mSkipMethod = env->GetMethodID(inputStreamClazz, "skip", "(J)J");
822 LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mSkipMethod == NULL, "Can't find skip method");
823
824 jclass inputBufferClazz = env->FindClass("java/nio/ByteBuffer");
825 LOG_ALWAYS_FATAL_IF(inputBufferClazz == NULL, "Can't find java/nio/ByteBuffer class");
826 gInputByteBufferClassInfo.mGetMethod = env->GetMethodID(inputBufferClazz, "get",
827 "([BII)Ljava/nio/ByteBuffer;");
828 LOG_ALWAYS_FATAL_IF(gInputByteBufferClassInfo.mGetMethod == NULL, "Can't find get method");
Ruben Brunkf967a542014-04-28 16:31:11 -0700829}
830
831static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700832 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700833 ALOGV("%s:", __FUNCTION__);
834 CameraMetadata characteristics;
835 CameraMetadata results;
836 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
837 jniThrowException(env, "java/lang/AssertionError",
838 "No native metadata defined for camera characteristics.");
839 return;
840 }
841 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
842 jniThrowException(env, "java/lang/AssertionError",
843 "No native metadata defined for capture results.");
844 return;
845 }
846
Ruben Brunk47e91f22014-05-28 18:38:42 -0700847 sp<NativeContext> nativeContext = new NativeContext();
848 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700849
850 writer->addIfd(TIFF_IFD_0);
851
852 status_t err = OK;
853
854 const uint32_t samplesPerPixel = 1;
855 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
856 const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
857 uint32_t imageWidth = 0;
858 uint32_t imageHeight = 0;
859
860 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -0700861 uint8_t cfaPlaneColor[3] = {0, 1, 2};
862 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -0700863
864 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700865 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -0700866
867 // Setup main image tags
868
Ruben Brunkf967a542014-04-28 16:31:11 -0700869 {
870 // Set orientation
871 uint16_t orientation = 1; // Normal
872 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700873 TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700874 }
875
876 {
877 // Set subfiletype
878 uint32_t subfileType = 0; // Main image
879 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700880 TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700881 }
882
883 {
884 // Set bits per sample
885 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
886 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700887 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700888 }
889
890 {
891 // Set compression
892 uint16_t compression = 1; // None
893 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700894 TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700895 }
896
897 {
898 // Set dimensions
899 camera_metadata_entry entry =
900 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700901 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700902 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
903 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
904 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700905 TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700906 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700907 TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700908 imageWidth = width;
909 imageHeight = height;
910 }
911
912 {
913 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -0700914 uint16_t interpretation = 32803; // CFA
Ruben Brunkf967a542014-04-28 16:31:11 -0700915 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700916 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700917 }
918
919 {
920 // Set blacklevel tags
921 camera_metadata_entry entry =
922 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700923 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700924 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
925 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700926 TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700927
928 uint16_t repeatDim[2] = {2, 2};
929 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700930 TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700931 }
932
933 {
934 // Set samples per pixel
935 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
936 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700937 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700938 }
939
940 {
941 // Set planar configuration
942 uint16_t config = 1; // Chunky
943 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700944 env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700945 }
946
947 {
948 // Set CFA pattern dimensions
949 uint16_t repeatDim[2] = {2, 2};
950 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700951 env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700952 }
953
954 {
955 // Set CFA pattern
956 camera_metadata_entry entry =
957 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700958 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700959
960 const int cfaLength = 4;
961 cfaEnum = entry.data.u8[0];
962 uint8_t cfa[cfaLength];
963 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
964 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
965 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -0700966 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700967
968 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
969 TAG_CFAPATTERN, writer);
970
971 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -0700972 }
973
974 {
975 // Set CFA plane color
Ruben Brunkf967a542014-04-28 16:31:11 -0700976 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700977 env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700978 }
979
980 {
981 // Set CFA layout
982 uint16_t cfaLayout = 1;
983 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700984 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700985 }
986
987 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700988 // image description
989 uint8_t imageDescription = '\0'; // empty
990 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700991 env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700992 }
993
994 {
995 // make
996 char manufacturer[PROPERTY_VALUE_MAX];
997
998 // Use "" to represent unknown make as suggested in TIFF/EP spec.
999 property_get("ro.product.manufacturer", manufacturer, "");
1000 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1001
1002 BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001003 TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001004 }
1005
1006 {
1007 // model
1008 char model[PROPERTY_VALUE_MAX];
1009
1010 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1011 property_get("ro.product.model", model, "");
1012 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1013
1014 BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001015 TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001016 }
1017
1018 {
1019 // x resolution
1020 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1021 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001022 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001023
1024 // y resolution
1025 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1026 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001027 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001028
1029 uint16_t unit = 2; // inches
1030 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001031 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001032 }
1033
1034 {
1035 // software
1036 char software[PROPERTY_VALUE_MAX];
1037 property_get("ro.build.fingerprint", software, "");
1038 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
1039 BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001040 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001041 }
1042
1043 {
1044 // datetime
1045 const size_t DATETIME_COUNT = 20;
1046 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
1047
1048 size_t len = strlen(captureTime) + 1;
1049 if (len != DATETIME_COUNT) {
1050 jniThrowException(env, "java/lang/IllegalArgumentException",
1051 "Timestamp string length is not required 20 characters");
1052 return;
1053 }
1054
Ruben Brunk47e91f22014-05-28 18:38:42 -07001055 if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
1056 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1057 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1058 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1059 "Invalid metadata for tag %x", TAG_DATETIME);
1060 return;
1061 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001062
1063 // datetime original
Ruben Brunk47e91f22014-05-28 18:38:42 -07001064 if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
1065 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1066 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1067 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1068 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
1069 return;
1070 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001071 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1072 }
1073
1074 {
1075 // TIFF/EP standard id
1076 uint8_t standardId[] = { 1, 0, 0, 0 };
1077 BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001078 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001079 }
1080
1081 {
1082 // copyright
1083 uint8_t copyright = '\0'; // empty
1084 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001085 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001086 }
1087
1088 {
1089 // exposure time
1090 camera_metadata_entry entry =
1091 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001092 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001093
1094 int64_t exposureTime = *(entry.data.i64);
1095
1096 if (exposureTime < 0) {
1097 // Should be unreachable
1098 jniThrowException(env, "java/lang/IllegalArgumentException",
1099 "Negative exposure time in metadata");
1100 return;
1101 }
1102
1103 // Ensure exposure time doesn't overflow (for exposures > 4s)
1104 uint32_t denominator = 1000000000;
1105 while (exposureTime > UINT32_MAX) {
1106 exposureTime >>= 1;
1107 denominator >>= 1;
1108 if (denominator == 0) {
1109 // Should be unreachable
1110 jniThrowException(env, "java/lang/IllegalArgumentException",
1111 "Exposure time too long");
1112 return;
1113 }
1114 }
1115
1116 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
1117 BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001118 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001119
1120 }
1121
1122 {
1123 // ISO speed ratings
1124 camera_metadata_entry entry =
1125 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001126 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001127
1128 int32_t tempIso = *(entry.data.i32);
1129 if (tempIso < 0) {
1130 jniThrowException(env, "java/lang/IllegalArgumentException",
1131 "Negative ISO value");
1132 return;
1133 }
1134
1135 if (tempIso > UINT16_MAX) {
1136 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1137 tempIso = UINT16_MAX;
1138 }
1139
1140 uint16_t iso = static_cast<uint16_t>(tempIso);
1141 BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001142 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001143 }
1144
1145 {
1146 // focal length
1147 camera_metadata_entry entry =
1148 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001149 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001150
1151 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1152 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001153 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001154 }
1155
1156 {
1157 // f number
1158 camera_metadata_entry entry =
1159 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001160 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001161
1162 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1163 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001164 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001165 }
1166
1167 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001168 // Set DNG version information
1169 uint8_t version[4] = {1, 4, 0, 0};
1170 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001171 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001172
1173 uint8_t backwardVersion[4] = {1, 1, 0, 0};
1174 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001175 env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001176 }
1177
1178 {
1179 // Set whitelevel
1180 camera_metadata_entry entry =
1181 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001182 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001183 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
1184 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001185 TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001186 }
1187
1188 {
1189 // Set default scale
1190 uint32_t defaultScale[4] = {1, 1, 1, 1};
1191 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001192 env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001193 }
1194
1195 bool singleIlluminant = false;
1196 {
1197 // Set calibration illuminants
1198 camera_metadata_entry entry1 =
1199 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001200 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001201 camera_metadata_entry entry2 =
1202 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1203 if (entry2.count == 0) {
1204 singleIlluminant = true;
1205 }
1206 uint16_t ref1 = entry1.data.u8[0];
1207
1208 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001209 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001210
1211 if (!singleIlluminant) {
1212 uint16_t ref2 = entry2.data.u8[0];
1213 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001214 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001215 }
1216 }
1217
1218 {
1219 // Set color transforms
1220 camera_metadata_entry entry1 =
1221 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001222 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001223
1224 int32_t colorTransform1[entry1.count * 2];
1225
1226 size_t ctr = 0;
1227 for(size_t i = 0; i < entry1.count; ++i) {
1228 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1229 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1230 }
1231
Ruben Brunk47e91f22014-05-28 18:38:42 -07001232 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1233 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001234
1235 if (!singleIlluminant) {
1236 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001237 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001238 int32_t colorTransform2[entry2.count * 2];
1239
1240 ctr = 0;
1241 for(size_t i = 0; i < entry2.count; ++i) {
1242 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1243 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1244 }
1245
Ruben Brunk47e91f22014-05-28 18:38:42 -07001246 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1247 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001248 }
1249 }
1250
1251 {
1252 // Set calibration transforms
1253 camera_metadata_entry entry1 =
1254 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001255 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001256
1257 int32_t calibrationTransform1[entry1.count * 2];
1258
1259 size_t ctr = 0;
1260 for(size_t i = 0; i < entry1.count; ++i) {
1261 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1262 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1263 }
1264
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001265 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1266 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001267
1268 if (!singleIlluminant) {
1269 camera_metadata_entry entry2 =
1270 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001271 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001272 int32_t calibrationTransform2[entry2.count * 2];
1273
1274 ctr = 0;
1275 for(size_t i = 0; i < entry2.count; ++i) {
1276 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1277 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1278 }
1279
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001280 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
1281 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001282 }
1283 }
1284
1285 {
1286 // Set forward transforms
1287 camera_metadata_entry entry1 =
1288 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001289 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001290
1291 int32_t forwardTransform1[entry1.count * 2];
1292
1293 size_t ctr = 0;
1294 for(size_t i = 0; i < entry1.count; ++i) {
1295 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1296 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1297 }
1298
1299 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001300 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001301
1302 if (!singleIlluminant) {
1303 camera_metadata_entry entry2 =
1304 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001305 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001306 int32_t forwardTransform2[entry2.count * 2];
1307
1308 ctr = 0;
1309 for(size_t i = 0; i < entry2.count; ++i) {
1310 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1311 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1312 }
1313
1314 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001315 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001316 }
1317 }
1318
1319 {
1320 // Set camera neutral
1321 camera_metadata_entry entry =
1322 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001323 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001324 uint32_t cameraNeutral[entry.count * 2];
1325
1326 size_t ctr = 0;
1327 for(size_t i = 0; i < entry.count; ++i) {
1328 cameraNeutral[ctr++] =
1329 static_cast<uint32_t>(entry.data.r[i].numerator);
1330 cameraNeutral[ctr++] =
1331 static_cast<uint32_t>(entry.data.r[i].denominator);
1332 }
1333
1334 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001335 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001336 }
1337
1338 {
1339 // Setup data strips
1340 // TODO: Switch to tiled implementation.
Ruben Brunk47e91f22014-05-28 18:38:42 -07001341 if (writer->addStrip(TIFF_IFD_0) != OK) {
1342 ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
1343 jniThrowException(env, "java/lang/IllegalStateException",
1344 "Failed to setup strip tags.");
1345 return;
1346 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001347 }
1348
1349 {
1350 // Setup default crop + crop origin tags
1351 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
1352 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
1353 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
1354 uint32_t defaultCropOrigin[] = {margin, margin};
1355 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
1356 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001357 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001358 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001359 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001360 }
1361 }
1362
1363 {
1364 // Setup unique camera model tag
1365 char model[PROPERTY_VALUE_MAX];
1366 property_get("ro.product.model", model, "");
1367
1368 char manufacturer[PROPERTY_VALUE_MAX];
1369 property_get("ro.product.manufacturer", manufacturer, "");
1370
1371 char brand[PROPERTY_VALUE_MAX];
1372 property_get("ro.product.brand", brand, "");
1373
1374 String8 cameraModel(model);
1375 cameraModel += "-";
1376 cameraModel += manufacturer;
1377 cameraModel += "-";
1378 cameraModel += brand;
1379
1380 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1381 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001382 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001383 }
1384
1385 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001386 // Setup sensor noise model
1387 camera_metadata_entry entry =
1388 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1389
Ruben Brunkd70132c2014-08-22 16:24:49 -07001390 const status_t numPlaneColors = 3;
1391 const status_t numCfaChannels = 4;
1392
1393 uint8_t cfaOut[numCfaChannels];
1394 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1395 jniThrowException(env, "java/lang/IllegalArgumentException",
1396 "Invalid CFA from camera characteristics");
1397 return;
1398 }
1399
1400 double noiseProfile[numPlaneColors * 2];
1401
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001402 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001403 if (entry.count != numCfaChannels * 2) {
1404 ALOGW("%s: Invalid entry count %u for noise profile returned in characteristics,"
1405 " no noise profile tag written...", __FUNCTION__, entry.count);
1406 } else {
1407 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1408 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1409
1410 BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
1411 noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
1412 } else {
1413 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1414 " tag written...", __FUNCTION__);
1415 }
1416 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001417 } else {
1418 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1419 __FUNCTION__);
1420 }
1421 }
1422
1423 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001424 // Setup opcode List 2
1425 camera_metadata_entry entry1 =
1426 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001427
1428 uint32_t lsmWidth = 0;
1429 uint32_t lsmHeight = 0;
1430
1431 if (entry1.count != 0) {
1432 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1433 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1434 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001435
1436 camera_metadata_entry entry2 =
1437 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001438
1439 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001440
1441 OpcodeListBuilder builder;
1442 status_t err = builder.addGainMapsForMetadata(lsmWidth,
1443 lsmHeight,
1444 0,
1445 0,
1446 imageHeight,
1447 imageWidth,
1448 opcodeCfaLayout,
1449 entry2.data.f);
1450 if (err == OK) {
1451 size_t listSize = builder.getSize();
1452 uint8_t opcodeListBuf[listSize];
1453 err = builder.buildOpList(opcodeListBuf);
1454 if (err == OK) {
1455 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001456 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001457 } else {
1458 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
1459 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
1460 }
1461 } else {
1462 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1463 jniThrowRuntimeException(env, "failed to add lens shading map.");
1464 }
1465 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001466 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1467 __FUNCTION__);
Ruben Brunkf967a542014-04-28 16:31:11 -07001468 }
1469 }
1470
Ruben Brunk47e91f22014-05-28 18:38:42 -07001471 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001472}
1473
1474static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1475 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001476 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001477}
1478
Ruben Brunk47e91f22014-05-28 18:38:42 -07001479static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001480 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001481
1482 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1483 if (writer == NULL) {
1484 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1485 jniThrowException(env, "java/lang/AssertionError",
1486 "setOrientation called with uninitialized DngCreator");
1487 return;
1488 }
1489
1490 uint16_t orientation = static_cast<uint16_t>(orient);
1491 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1492 TAG_ORIENTATION, writer);
1493
1494 // Set main image orientation also if in a separate IFD
1495 if (writer->hasIfd(TIFF_IFD_SUB1)) {
1496 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1497 TAG_ORIENTATION, writer);
1498 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001499}
1500
Ruben Brunk47e91f22014-05-28 18:38:42 -07001501static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001502 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001503
1504 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1505 if (writer == NULL) {
1506 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1507 jniThrowException(env, "java/lang/AssertionError",
1508 "setDescription called with uninitialized DngCreator");
1509 return;
1510 }
1511
1512 const char* desc = env->GetStringUTFChars(description, NULL);
1513 size_t len = strlen(desc) + 1;
1514
1515 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1516 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1517 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1518 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1519 }
1520
1521 env->ReleaseStringUTFChars(description, desc);
Ruben Brunkf967a542014-04-28 16:31:11 -07001522}
1523
Ruben Brunk47e91f22014-05-28 18:38:42 -07001524static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1525 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001526 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001527
1528 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1529 if (writer == NULL) {
1530 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1531 jniThrowException(env, "java/lang/AssertionError",
1532 "setGpsTags called with uninitialized DngCreator");
1533 return;
1534 }
1535
1536 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1537 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1538 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1539 TIFF_IFD_0);
1540 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1541 return;
1542 }
1543 }
1544
1545 const jsize GPS_VALUE_LENGTH = 6;
1546 jsize latLen = env->GetArrayLength(latTag);
1547 jsize longLen = env->GetArrayLength(longTag);
1548 jsize timeLen = env->GetArrayLength(timeTag);
1549 if (latLen != GPS_VALUE_LENGTH) {
1550 jniThrowException(env, "java/lang/IllegalArgumentException",
1551 "invalid latitude tag length");
1552 return;
1553 } else if (longLen != GPS_VALUE_LENGTH) {
1554 jniThrowException(env, "java/lang/IllegalArgumentException",
1555 "invalid longitude tag length");
1556 return;
1557 } else if (timeLen != GPS_VALUE_LENGTH) {
1558 jniThrowException(env, "java/lang/IllegalArgumentException",
1559 "invalid time tag length");
1560 return;
1561 }
1562
1563 uint32_t latitude[GPS_VALUE_LENGTH];
1564 uint32_t longitude[GPS_VALUE_LENGTH];
1565 uint32_t timestamp[GPS_VALUE_LENGTH];
1566
1567 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1568 reinterpret_cast<jint*>(&latitude));
1569 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1570 reinterpret_cast<jint*>(&longitude));
1571 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1572 reinterpret_cast<jint*>(&timestamp));
1573
1574 const jsize GPS_REF_LENGTH = 2;
1575 const jsize GPS_DATE_LENGTH = 11;
1576 uint8_t latitudeRef[GPS_REF_LENGTH];
1577 uint8_t longitudeRef[GPS_REF_LENGTH];
1578 uint8_t date[GPS_DATE_LENGTH];
1579
1580 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1581 latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1582 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1583 longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1584
1585 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1586 date[GPS_DATE_LENGTH - 1] = '\0';
1587
1588 {
1589 uint8_t version[] = {2, 3, 0, 0};
1590 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1591 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1592 }
1593
1594 {
1595 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1596 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1597 }
1598
1599 {
1600 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1601 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1602 }
1603
1604 {
1605 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1606 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1607 }
1608
1609 {
1610 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1611 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1612 }
1613
1614 {
1615 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1616 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1617 }
1618
1619 {
1620 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1621 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1622 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001623}
1624
Ruben Brunk47e91f22014-05-28 18:38:42 -07001625static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1626 jint height) {
1627 ALOGV("%s:", __FUNCTION__);
1628
1629 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1630 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1631 if (writer == NULL || context == NULL) {
1632 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1633 jniThrowException(env, "java/lang/AssertionError",
1634 "setThumbnail called with uninitialized DngCreator");
1635 return;
1636 }
1637
1638 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1639 jlong capacity = env->GetDirectBufferCapacity(buffer);
1640 if (capacity != fullSize) {
1641 jniThrowExceptionFmt(env, "java/lang/AssertionError",
1642 "Invalid size %d for thumbnail, expected size was %d",
1643 capacity, fullSize);
1644 return;
1645 }
1646
1647 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1648 if (pixelBytes == NULL) {
1649 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1650 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1651 return;
1652 }
1653
1654 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1655 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1656 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1657 TIFF_IFD_0);
1658 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1659 return;
1660 }
1661
1662 Vector<uint16_t> tagsToMove;
1663 tagsToMove.add(TAG_ORIENTATION);
1664 tagsToMove.add(TAG_NEWSUBFILETYPE);
1665 tagsToMove.add(TAG_BITSPERSAMPLE);
1666 tagsToMove.add(TAG_COMPRESSION);
1667 tagsToMove.add(TAG_IMAGEWIDTH);
1668 tagsToMove.add(TAG_IMAGELENGTH);
1669 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1670 tagsToMove.add(TAG_BLACKLEVEL);
1671 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1672 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1673 tagsToMove.add(TAG_PLANARCONFIGURATION);
1674 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1675 tagsToMove.add(TAG_CFAPATTERN);
1676 tagsToMove.add(TAG_CFAPLANECOLOR);
1677 tagsToMove.add(TAG_CFALAYOUT);
1678 tagsToMove.add(TAG_XRESOLUTION);
1679 tagsToMove.add(TAG_YRESOLUTION);
1680 tagsToMove.add(TAG_RESOLUTIONUNIT);
1681 tagsToMove.add(TAG_WHITELEVEL);
1682 tagsToMove.add(TAG_DEFAULTSCALE);
1683 tagsToMove.add(TAG_ROWSPERSTRIP);
1684 tagsToMove.add(TAG_STRIPBYTECOUNTS);
1685 tagsToMove.add(TAG_STRIPOFFSETS);
1686 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1687 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1688 tagsToMove.add(TAG_OPCODELIST2);
1689
1690 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1691 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1692 return;
1693 }
1694
1695 // Make sure both IFDs get the same orientation tag
1696 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1697 if (orientEntry != NULL) {
1698 writer->addEntry(orientEntry, TIFF_IFD_0);
1699 }
1700 }
1701
1702 // Setup thumbnail tags
1703
1704 {
1705 // Set photometric interpretation
1706 uint16_t interpretation = 2; // RGB
1707 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1708 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1709 }
1710
1711 {
1712 // Set planar configuration
1713 uint16_t config = 1; // Chunky
1714 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1715 env, TAG_PLANARCONFIGURATION, writer);
1716 }
1717
1718 {
1719 // Set samples per pixel
1720 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1721 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1722 env, TAG_SAMPLESPERPIXEL, writer);
1723 }
1724
1725 {
1726 // Set bits per sample
1727 uint16_t bits = BITS_PER_RGB_SAMPLE;
1728 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1729 TAG_BITSPERSAMPLE, writer);
1730 }
1731
1732 {
1733 // Set subfiletype
1734 uint32_t subfileType = 1; // Thumbnail image
1735 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1736 TAG_NEWSUBFILETYPE, writer);
1737 }
1738
1739 {
1740 // Set compression
1741 uint16_t compression = 1; // None
1742 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1743 TAG_COMPRESSION, writer);
1744 }
1745
1746 {
1747 // Set dimensions
1748 uint32_t uWidth = static_cast<uint32_t>(width);
1749 uint32_t uHeight = static_cast<uint32_t>(height);
1750 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1751 TAG_IMAGEWIDTH, writer);
1752 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1753 TAG_IMAGELENGTH, writer);
1754 }
1755
1756 {
1757 // x resolution
1758 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1759 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1760 env, TAG_XRESOLUTION, writer);
1761
1762 // y resolution
1763 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1764 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1765 env, TAG_YRESOLUTION, writer);
1766
1767 uint16_t unit = 2; // inches
1768 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1769 env, TAG_RESOLUTIONUNIT, writer);
1770 }
1771
1772 {
1773 // Setup data strips
1774 if (writer->addStrip(TIFF_IFD_0) != OK) {
1775 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1776 jniThrowException(env, "java/lang/IllegalStateException",
1777 "Failed to setup thumbnail strip tags.");
1778 return;
1779 }
1780 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1781 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1782 jniThrowException(env, "java/lang/IllegalStateException",
1783 "Failed to setup main image strip tags.");
1784 return;
1785 }
1786 }
1787
1788 if (!context->setThumbnail(pixelBytes, width, height)) {
1789 jniThrowException(env, "java/lang/IllegalStateException",
1790 "Failed to set thumbnail.");
1791 return;
1792 }
1793}
1794
1795// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07001796static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001797 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1798 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001799 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001800 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d,"
1801 " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset);
1802 uint32_t rStride = static_cast<uint32_t>(rowStride);
1803 uint32_t pStride = static_cast<uint32_t>(pixStride);
1804 uint32_t uWidth = static_cast<uint32_t>(width);
1805 uint32_t uHeight = static_cast<uint32_t>(height);
1806 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07001807
1808 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1809 if(env->ExceptionCheck()) {
1810 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1811 return;
1812 }
1813
Ruben Brunkf967a542014-04-28 16:31:11 -07001814 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001815 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1816 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001817 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1818 jniThrowException(env, "java/lang/AssertionError",
1819 "Write called with uninitialized DngCreator");
1820 return;
1821 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001822
1823 // Validate DNG header
1824 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001825 return;
1826 }
1827
Ruben Brunk47e91f22014-05-28 18:38:42 -07001828 sp<JniInputByteBuffer> inBuf;
1829 Vector<StripSource*> sources;
1830 sp<DirectStripSource> thumbnailSource;
1831 uint32_t targetIfd = TIFF_IFD_0;
1832
1833 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1834
1835 if (hasThumbnail) {
1836 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1837 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1838 uint32_t thumbWidth = context->getThumbnailWidth();
1839 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1840 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1841 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1842 SAMPLES_PER_RGB_PIXEL);
1843 sources.add(thumbnailSource.get());
1844 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001845 }
1846
Ruben Brunk47e91f22014-05-28 18:38:42 -07001847 if (isDirect) {
1848 size_t fullSize = rStride * uHeight;
1849 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1850 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1851 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1852 "Invalid size %d for Image, size given in metadata is %d at current stride",
1853 capacity, fullSize);
1854 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001855 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001856
Ruben Brunk47e91f22014-05-28 18:38:42 -07001857 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1858 if (pixelBytes == NULL) {
1859 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1860 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1861 return;
1862 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001863
Ruben Brunk47e91f22014-05-28 18:38:42 -07001864 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1865 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1866 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1867 sources.add(&stripSource);
1868
1869 status_t ret = OK;
1870 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1871 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001872 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001873 jniThrowExceptionFmt(env, "java/io/IOException",
1874 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001875 }
1876 return;
1877 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001878 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001879 inBuf = new JniInputByteBuffer(env, inBuffer);
1880
1881 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1882 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1883 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1884 sources.add(&stripSource);
1885
1886 status_t ret = OK;
1887 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1888 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1889 if (!env->ExceptionCheck()) {
1890 jniThrowExceptionFmt(env, "java/io/IOException",
1891 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001892 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001893 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001894 }
1895 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001896}
1897
1898static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001899 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001900 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001901
1902 uint32_t rowStride = width * BYTES_PER_SAMPLE;
1903 uint32_t pixStride = BYTES_PER_SAMPLE;
1904 uint32_t uWidth = static_cast<uint32_t>(width);
1905 uint32_t uHeight = static_cast<uint32_t>(height);
1906 uint64_t uOffset = static_cast<uint32_t>(offset);
1907
1908 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u,"
1909 "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride,
1910 offset);
1911
1912 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1913 if(env->ExceptionCheck()) {
1914 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1915 return;
1916 }
1917
1918 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1919 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1920 if (writer == NULL || context == NULL) {
1921 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1922 jniThrowException(env, "java/lang/AssertionError",
1923 "Write called with uninitialized DngCreator");
1924 return;
1925 }
1926
1927 // Validate DNG header
1928 if (!validateDngHeader(env, writer, width, height)) {
1929 return;
1930 }
1931
1932 sp<DirectStripSource> thumbnailSource;
1933 uint32_t targetIfd = TIFF_IFD_0;
1934 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1935 Vector<StripSource*> sources;
1936
1937 if (hasThumbnail) {
1938 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1939 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1940 uint32_t width = context->getThumbnailWidth();
1941 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1942 width, context->getThumbnailHeight(), bytesPerPixel,
1943 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1944 SAMPLES_PER_RGB_PIXEL);
1945 sources.add(thumbnailSource.get());
1946 targetIfd = TIFF_IFD_SUB1;
1947 }
1948
1949 sp<JniInputStream> in = new JniInputStream(env, inStream);
1950
1951 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1952 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
1953 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1954 sources.add(&stripSource);
1955
1956 status_t ret = OK;
1957 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1958 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1959 if (!env->ExceptionCheck()) {
1960 jniThrowExceptionFmt(env, "java/io/IOException",
1961 "Encountered error %d while writing file.", ret);
1962 }
1963 return;
1964 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001965}
1966
1967} /*extern "C" */
1968
1969static JNINativeMethod gDngCreatorMethods[] = {
1970 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
1971 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001972 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1973 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07001974 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
1975 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001976 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
1977 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
1978 (void*) DngCreator_nativeSetGpsTags},
1979 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
1980 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001981 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001982 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001983 (void*) DngCreator_nativeWriteInputStream},
1984};
1985
Ruben Brunkb6079002014-05-22 12:33:54 -07001986int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001987 return AndroidRuntime::registerNativeMethods(env,
Ruben Brunkb6079002014-05-22 12:33:54 -07001988 "android/hardware/camera2/DngCreator", gDngCreatorMethods,
1989 NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07001990}