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