blob: bb8de28e3c5ef245c2096e5b4244254df546f820 [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"
Dan Albert46d84442014-11-18 16:07:51 -080019#include <inttypes.h>
20#include <string.h>
21
22#include <utils/Log.h>
23#include <utils/Errors.h>
24#include <utils/StrongPointer.h>
25#include <utils/RefBase.h>
26#include <utils/Vector.h>
27#include <cutils/properties.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070028
29#include <system/camera_metadata.h>
30#include <camera/CameraMetadata.h>
31#include <img_utils/DngUtils.h>
32#include <img_utils/TagDefinitions.h>
33#include <img_utils/TiffIfd.h>
34#include <img_utils/TiffWriter.h>
35#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070036#include <img_utils/Input.h>
37#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070038
Andreas Gampeed6b9df2014-11-20 22:02:20 -080039#include "core_jni_helpers.h"
Ruben Brunkf967a542014-04-28 16:31:11 -070040#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) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700430 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700431 jlong offset = mOffset;
432
433 if (fullSize != count) {
434 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
435 fullSize);
436 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
437 return BAD_VALUE;
438 }
439
440 // Skip offset
441 while (offset > 0) {
442 ssize_t skipped = mInput->skip(offset);
443 if (skipped <= 0) {
444 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
445 jniThrowExceptionFmt(mEnv, "java/io/IOException",
446 "Early EOF encountered in skip, not enough pixel data for image of size %u",
447 fullSize);
448 skipped = NOT_ENOUGH_DATA;
449 } else {
450 if (!mEnv->ExceptionCheck()) {
451 jniThrowException(mEnv, "java/io/IOException",
452 "Error encountered while skip bytes in input stream.");
453 }
454 }
455
456 return skipped;
457 }
458 offset -= skipped;
459 }
460
461 Vector<uint8_t> row;
462 if (row.resize(mRowStride) < 0) {
463 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
464 return BAD_VALUE;
465 }
466
467 uint8_t* rowBytes = row.editArray();
468
469 for (uint32_t i = 0; i < mHeight; ++i) {
470 size_t rowFillAmt = 0;
471 size_t rowSize = mPixStride;
472
473 while (rowFillAmt < mRowStride) {
474 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
475 if (bytesRead <= 0) {
476 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
477 jniThrowExceptionFmt(mEnv, "java/io/IOException",
478 "Early EOF encountered, not enough pixel data for image of size %u",
479 fullSize);
480 bytesRead = NOT_ENOUGH_DATA;
481 } else {
482 if (!mEnv->ExceptionCheck()) {
483 jniThrowException(mEnv, "java/io/IOException",
484 "Error encountered while reading");
485 }
486 }
487 return bytesRead;
488 }
489 rowFillAmt += bytesRead;
490 rowSize -= bytesRead;
491 }
492
493 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
494 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
495
496 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
497 mEnv->ExceptionCheck()) {
498 if (!mEnv->ExceptionCheck()) {
499 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
500 }
501 return BAD_VALUE;
502 }
503 } else {
504 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
505 jniThrowException(mEnv, "java/lang/IllegalStateException",
506 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
507 return BAD_VALUE;
508
509 // TODO: Add support for non-contiguous pixels if needed.
510 }
511 }
512 return OK;
513}
514
515uint32_t InputStripSource::getIfd() const {
516 return mIfd;
517}
518
519// End of InputStripSource
520// ----------------------------------------------------------------------------
521
522/**
523 * StripSource subclass for direct buffer types.
524 *
525 * This class is not intended to be used across JNI calls.
526 */
527
528class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
529public:
530 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
531 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
532 uint32_t bytesPerSample, uint32_t samplesPerPixel);
533
534 virtual ~DirectStripSource();
535
536 virtual status_t writeToStream(Output& stream, uint32_t count);
537
538 virtual uint32_t getIfd() const;
539protected:
540 uint32_t mIfd;
541 const uint8_t* mPixelBytes;
542 uint32_t mWidth;
543 uint32_t mHeight;
544 uint32_t mPixStride;
545 uint32_t mRowStride;
546 uint16_t mOffset;
547 JNIEnv* mEnv;
548 uint32_t mBytesPerSample;
549 uint32_t mSamplesPerPixel;
550};
551
552DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
553 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
554 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
555 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
556 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
557 mSamplesPerPixel(samplesPerPixel) {}
558
559DirectStripSource::~DirectStripSource() {}
560
561status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700562 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700563
564 if (fullSize != count) {
565 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
566 fullSize);
567 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
568 return BAD_VALUE;
569 }
570
571 if (mPixStride == mBytesPerSample * mSamplesPerPixel
572 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
573 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
574
575 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
576 if (!mEnv->ExceptionCheck()) {
577 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
578 }
579 return BAD_VALUE;
580 }
581 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
582 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
583
584 for (size_t i = 0; i < mHeight; ++i) {
585 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
586 mEnv->ExceptionCheck()) {
587 if (!mEnv->ExceptionCheck()) {
588 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
589 }
590 return BAD_VALUE;
591 }
592 }
593 } else {
594 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
595
596 jniThrowException(mEnv, "java/lang/IllegalStateException",
597 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
598 return BAD_VALUE;
599
600 // TODO: Add support for non-contiguous pixels if needed.
601 }
602 return OK;
603
604}
605
606uint32_t DirectStripSource::getIfd() const {
607 return mIfd;
608}
609
610// End of DirectStripSource
611// ----------------------------------------------------------------------------
612
613static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
614 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
615
616 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
617 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
618 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
619
620 if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
621 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
622 "Metadata width %d doesn't match image width %d", metadataWidth, width);
623 return false;
624 }
625
626 if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
627 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
628 "Metadata height %d doesn't match image height %d", metadataHeight, height);
629 return false;
630 }
631
632 return true;
633}
634
635static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
636 const Vector<uint16_t>& entries) {
637 for (size_t i = 0; i < entries.size(); ++i) {
638 uint16_t tagId = entries[i];
639 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
640 if (entry == NULL) {
641 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
642 ifdFrom);
643 return BAD_VALUE;
644 }
645 if (writer->addEntry(entry, ifdTo) != OK) {
646 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
647 ifdFrom);
648 return BAD_VALUE;
649 }
650 writer->removeEntry(tagId, ifdFrom);
651 }
652 return OK;
653}
654
Ruben Brunkd70132c2014-08-22 16:24:49 -0700655/**
656 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
657 * Returns OK on success, or a negative error code if the CFA enum was invalid.
658 */
659static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
660 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
661 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
662 cfaEnum);
663 switch(cfa) {
664 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
665 cfaOut[0] = 0;
666 cfaOut[1] = 1;
667 cfaOut[2] = 1;
668 cfaOut[3] = 2;
669 break;
670 }
671 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
672 cfaOut[0] = 1;
673 cfaOut[1] = 0;
674 cfaOut[2] = 2;
675 cfaOut[3] = 1;
676 break;
677 }
678 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
679 cfaOut[0] = 1;
680 cfaOut[1] = 2;
681 cfaOut[2] = 0;
682 cfaOut[3] = 1;
683 break;
684 }
685 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
686 cfaOut[0] = 2;
687 cfaOut[1] = 1;
688 cfaOut[2] = 1;
689 cfaOut[3] = 0;
690 break;
691 }
692 default: {
693 return BAD_VALUE;
694 }
695 }
696 return OK;
697}
698
699/**
700 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
701 * RGGB for an unknown enum.
702 */
703static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
704 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
705 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
706 cfaEnum);
707 switch(cfa) {
708 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
709 return OpcodeListBuilder::CFA_RGGB;
710 }
711 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
712 return OpcodeListBuilder::CFA_GRBG;
713 }
714 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
715 return OpcodeListBuilder::CFA_GBRG;
716 }
717 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
718 return OpcodeListBuilder::CFA_BGGR;
719 }
720 default: {
721 return OpcodeListBuilder::CFA_RGGB;
722 }
723 }
724}
725
726/**
727 * For each color plane, find the corresponding noise profile coefficients given in the
728 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
729 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
730 *
731 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
732 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
733 * coefficients.
734 * numChannels - the number of noise profile coefficient pairs and color channels given in
735 * the perChannelNoiseProfile and cfa arguments, respectively.
736 * planeColors - the color planes in the noise profile output.
737 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
738 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
739 *
740 * returns OK, or a negative error code on failure.
741 */
742static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
743 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
744 /*out*/double* noiseProfile) {
745
746 for (size_t p = 0; p < numPlanes; ++p) {
747 size_t S = p * 2;
748 size_t O = p * 2 + 1;
749
750 noiseProfile[S] = 0;
751 noiseProfile[O] = 0;
752 bool uninitialized = true;
753 for (size_t c = 0; c < numChannels; ++c) {
754 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
755 noiseProfile[S] = perChannelNoiseProfile[c * 2];
756 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
757 uninitialized = false;
758 }
759 }
760 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800761 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
762 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700763 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
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800806 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
807 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700808
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800809 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
810 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
811 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700812
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800813 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
814 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
815 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700816
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800817 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
818 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
819 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -0700820}
821
822static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700823 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700824 ALOGV("%s:", __FUNCTION__);
825 CameraMetadata characteristics;
826 CameraMetadata results;
827 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
828 jniThrowException(env, "java/lang/AssertionError",
829 "No native metadata defined for camera characteristics.");
830 return;
831 }
832 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
833 jniThrowException(env, "java/lang/AssertionError",
834 "No native metadata defined for capture results.");
835 return;
836 }
837
Ruben Brunk47e91f22014-05-28 18:38:42 -0700838 sp<NativeContext> nativeContext = new NativeContext();
839 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700840
841 writer->addIfd(TIFF_IFD_0);
842
843 status_t err = OK;
844
845 const uint32_t samplesPerPixel = 1;
846 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -0700847 uint32_t imageWidth = 0;
848 uint32_t imageHeight = 0;
849
850 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -0700851 uint8_t cfaPlaneColor[3] = {0, 1, 2};
852 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -0700853
854 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700855 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -0700856
857 // Setup main image tags
858
Ruben Brunkf967a542014-04-28 16:31:11 -0700859 {
860 // Set orientation
861 uint16_t orientation = 1; // Normal
862 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700863 TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700864 }
865
866 {
867 // Set subfiletype
868 uint32_t subfileType = 0; // Main image
869 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700870 TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700871 }
872
873 {
874 // Set bits per sample
875 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
876 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700877 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700878 }
879
880 {
881 // Set compression
882 uint16_t compression = 1; // None
883 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700884 TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700885 }
886
887 {
888 // Set dimensions
889 camera_metadata_entry entry =
890 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700891 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700892 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
893 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
894 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700895 TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700896 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700897 TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700898 imageWidth = width;
899 imageHeight = height;
900 }
901
902 {
903 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -0700904 uint16_t interpretation = 32803; // CFA
Ruben Brunkf967a542014-04-28 16:31:11 -0700905 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700906 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700907 }
908
909 {
910 // Set blacklevel tags
911 camera_metadata_entry entry =
912 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700913 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700914 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
915 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700916 TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700917
918 uint16_t repeatDim[2] = {2, 2};
919 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700920 TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700921 }
922
923 {
924 // Set samples per pixel
925 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
926 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700927 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700928 }
929
930 {
931 // Set planar configuration
932 uint16_t config = 1; // Chunky
933 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700934 env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700935 }
936
937 {
938 // Set CFA pattern dimensions
939 uint16_t repeatDim[2] = {2, 2};
940 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700941 env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700942 }
943
944 {
945 // Set CFA pattern
946 camera_metadata_entry entry =
947 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700948 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700949
950 const int cfaLength = 4;
951 cfaEnum = entry.data.u8[0];
952 uint8_t cfa[cfaLength];
953 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
954 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
955 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -0700956 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700957
958 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
959 TAG_CFAPATTERN, writer);
960
961 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -0700962 }
963
964 {
965 // Set CFA plane color
Ruben Brunkf967a542014-04-28 16:31:11 -0700966 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700967 env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700968 }
969
970 {
971 // Set CFA layout
972 uint16_t cfaLayout = 1;
973 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700974 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700975 }
976
977 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700978 // image description
979 uint8_t imageDescription = '\0'; // empty
980 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700981 env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700982 }
983
984 {
985 // make
986 char manufacturer[PROPERTY_VALUE_MAX];
987
988 // Use "" to represent unknown make as suggested in TIFF/EP spec.
989 property_get("ro.product.manufacturer", manufacturer, "");
990 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
991
992 BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700993 TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700994 }
995
996 {
997 // model
998 char model[PROPERTY_VALUE_MAX];
999
1000 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1001 property_get("ro.product.model", model, "");
1002 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1003
1004 BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001005 TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001006 }
1007
1008 {
1009 // x resolution
1010 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1011 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001012 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001013
1014 // y resolution
1015 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1016 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001017 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001018
1019 uint16_t unit = 2; // inches
1020 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001021 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001022 }
1023
1024 {
1025 // software
1026 char software[PROPERTY_VALUE_MAX];
1027 property_get("ro.build.fingerprint", software, "");
1028 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
1029 BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001030 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001031 }
1032
1033 {
1034 // datetime
1035 const size_t DATETIME_COUNT = 20;
1036 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
1037
1038 size_t len = strlen(captureTime) + 1;
1039 if (len != DATETIME_COUNT) {
1040 jniThrowException(env, "java/lang/IllegalArgumentException",
1041 "Timestamp string length is not required 20 characters");
1042 return;
1043 }
1044
Ruben Brunk47e91f22014-05-28 18:38:42 -07001045 if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
1046 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1047 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1048 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1049 "Invalid metadata for tag %x", TAG_DATETIME);
1050 return;
1051 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001052
1053 // datetime original
Ruben Brunk47e91f22014-05-28 18:38:42 -07001054 if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
1055 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1056 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1057 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1058 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
1059 return;
1060 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001061 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1062 }
1063
1064 {
1065 // TIFF/EP standard id
1066 uint8_t standardId[] = { 1, 0, 0, 0 };
1067 BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001068 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001069 }
1070
1071 {
1072 // copyright
1073 uint8_t copyright = '\0'; // empty
1074 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001075 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001076 }
1077
1078 {
1079 // exposure time
1080 camera_metadata_entry entry =
1081 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001082 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001083
1084 int64_t exposureTime = *(entry.data.i64);
1085
1086 if (exposureTime < 0) {
1087 // Should be unreachable
1088 jniThrowException(env, "java/lang/IllegalArgumentException",
1089 "Negative exposure time in metadata");
1090 return;
1091 }
1092
1093 // Ensure exposure time doesn't overflow (for exposures > 4s)
1094 uint32_t denominator = 1000000000;
1095 while (exposureTime > UINT32_MAX) {
1096 exposureTime >>= 1;
1097 denominator >>= 1;
1098 if (denominator == 0) {
1099 // Should be unreachable
1100 jniThrowException(env, "java/lang/IllegalArgumentException",
1101 "Exposure time too long");
1102 return;
1103 }
1104 }
1105
1106 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
1107 BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001108 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001109
1110 }
1111
1112 {
1113 // ISO speed ratings
1114 camera_metadata_entry entry =
1115 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001116 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001117
1118 int32_t tempIso = *(entry.data.i32);
1119 if (tempIso < 0) {
1120 jniThrowException(env, "java/lang/IllegalArgumentException",
1121 "Negative ISO value");
1122 return;
1123 }
1124
1125 if (tempIso > UINT16_MAX) {
1126 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1127 tempIso = UINT16_MAX;
1128 }
1129
1130 uint16_t iso = static_cast<uint16_t>(tempIso);
1131 BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001132 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001133 }
1134
1135 {
1136 // focal length
1137 camera_metadata_entry entry =
1138 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001139 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001140
1141 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1142 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001143 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001144 }
1145
1146 {
1147 // f number
1148 camera_metadata_entry entry =
1149 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001150 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001151
1152 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1153 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001154 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001155 }
1156
1157 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001158 // Set DNG version information
1159 uint8_t version[4] = {1, 4, 0, 0};
1160 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001161 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001162
1163 uint8_t backwardVersion[4] = {1, 1, 0, 0};
1164 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001165 env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001166 }
1167
1168 {
1169 // Set whitelevel
1170 camera_metadata_entry entry =
1171 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001172 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001173 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
1174 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001175 TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001176 }
1177
1178 {
1179 // Set default scale
1180 uint32_t defaultScale[4] = {1, 1, 1, 1};
1181 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001182 env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001183 }
1184
1185 bool singleIlluminant = false;
1186 {
1187 // Set calibration illuminants
1188 camera_metadata_entry entry1 =
1189 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001190 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001191 camera_metadata_entry entry2 =
1192 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1193 if (entry2.count == 0) {
1194 singleIlluminant = true;
1195 }
1196 uint16_t ref1 = entry1.data.u8[0];
1197
1198 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001199 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001200
1201 if (!singleIlluminant) {
1202 uint16_t ref2 = entry2.data.u8[0];
1203 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001204 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001205 }
1206 }
1207
1208 {
1209 // Set color transforms
1210 camera_metadata_entry entry1 =
1211 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001212 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001213
1214 int32_t colorTransform1[entry1.count * 2];
1215
1216 size_t ctr = 0;
1217 for(size_t i = 0; i < entry1.count; ++i) {
1218 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1219 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1220 }
1221
Ruben Brunk47e91f22014-05-28 18:38:42 -07001222 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1223 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001224
1225 if (!singleIlluminant) {
1226 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001227 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001228 int32_t colorTransform2[entry2.count * 2];
1229
1230 ctr = 0;
1231 for(size_t i = 0; i < entry2.count; ++i) {
1232 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1233 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1234 }
1235
Ruben Brunk47e91f22014-05-28 18:38:42 -07001236 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1237 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001238 }
1239 }
1240
1241 {
1242 // Set calibration transforms
1243 camera_metadata_entry entry1 =
1244 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001245 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001246
1247 int32_t calibrationTransform1[entry1.count * 2];
1248
1249 size_t ctr = 0;
1250 for(size_t i = 0; i < entry1.count; ++i) {
1251 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1252 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1253 }
1254
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001255 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1256 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001257
1258 if (!singleIlluminant) {
1259 camera_metadata_entry entry2 =
1260 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001261 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001262 int32_t calibrationTransform2[entry2.count * 2];
1263
1264 ctr = 0;
1265 for(size_t i = 0; i < entry2.count; ++i) {
1266 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1267 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1268 }
1269
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001270 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001271 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001272 }
1273 }
1274
1275 {
1276 // Set forward transforms
1277 camera_metadata_entry entry1 =
1278 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001279 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001280
1281 int32_t forwardTransform1[entry1.count * 2];
1282
1283 size_t ctr = 0;
1284 for(size_t i = 0; i < entry1.count; ++i) {
1285 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1286 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1287 }
1288
1289 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001290 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001291
1292 if (!singleIlluminant) {
1293 camera_metadata_entry entry2 =
1294 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001295 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001296 int32_t forwardTransform2[entry2.count * 2];
1297
1298 ctr = 0;
1299 for(size_t i = 0; i < entry2.count; ++i) {
1300 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1301 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1302 }
1303
1304 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001305 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001306 }
1307 }
1308
1309 {
1310 // Set camera neutral
1311 camera_metadata_entry entry =
1312 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001313 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001314 uint32_t cameraNeutral[entry.count * 2];
1315
1316 size_t ctr = 0;
1317 for(size_t i = 0; i < entry.count; ++i) {
1318 cameraNeutral[ctr++] =
1319 static_cast<uint32_t>(entry.data.r[i].numerator);
1320 cameraNeutral[ctr++] =
1321 static_cast<uint32_t>(entry.data.r[i].denominator);
1322 }
1323
1324 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001325 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001326 }
1327
1328 {
1329 // Setup data strips
1330 // TODO: Switch to tiled implementation.
Ruben Brunk47e91f22014-05-28 18:38:42 -07001331 if (writer->addStrip(TIFF_IFD_0) != OK) {
1332 ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
1333 jniThrowException(env, "java/lang/IllegalStateException",
1334 "Failed to setup strip tags.");
1335 return;
1336 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001337 }
1338
1339 {
1340 // Setup default crop + crop origin tags
1341 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
1342 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
1343 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
1344 uint32_t defaultCropOrigin[] = {margin, margin};
1345 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
1346 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001347 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001348 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001349 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001350 }
1351 }
1352
1353 {
1354 // Setup unique camera model tag
1355 char model[PROPERTY_VALUE_MAX];
1356 property_get("ro.product.model", model, "");
1357
1358 char manufacturer[PROPERTY_VALUE_MAX];
1359 property_get("ro.product.manufacturer", manufacturer, "");
1360
1361 char brand[PROPERTY_VALUE_MAX];
1362 property_get("ro.product.brand", brand, "");
1363
1364 String8 cameraModel(model);
1365 cameraModel += "-";
1366 cameraModel += manufacturer;
1367 cameraModel += "-";
1368 cameraModel += brand;
1369
1370 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1371 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001372 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001373 }
1374
1375 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001376 // Setup sensor noise model
1377 camera_metadata_entry entry =
1378 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1379
Ruben Brunkd70132c2014-08-22 16:24:49 -07001380 const status_t numPlaneColors = 3;
1381 const status_t numCfaChannels = 4;
1382
1383 uint8_t cfaOut[numCfaChannels];
1384 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1385 jniThrowException(env, "java/lang/IllegalArgumentException",
1386 "Invalid CFA from camera characteristics");
1387 return;
1388 }
1389
1390 double noiseProfile[numPlaneColors * 2];
1391
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001392 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001393 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001394 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1395 "in characteristics, no noise profile tag written...",
1396 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001397 } else {
1398 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1399 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1400
1401 BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
1402 noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
1403 } else {
1404 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1405 " tag written...", __FUNCTION__);
1406 }
1407 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001408 } else {
1409 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1410 __FUNCTION__);
1411 }
1412 }
1413
1414 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001415 // Setup opcode List 2
1416 camera_metadata_entry entry1 =
1417 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001418
1419 uint32_t lsmWidth = 0;
1420 uint32_t lsmHeight = 0;
1421
1422 if (entry1.count != 0) {
1423 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1424 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1425 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001426
1427 camera_metadata_entry entry2 =
1428 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001429
1430 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001431
1432 OpcodeListBuilder builder;
1433 status_t err = builder.addGainMapsForMetadata(lsmWidth,
1434 lsmHeight,
1435 0,
1436 0,
1437 imageHeight,
1438 imageWidth,
1439 opcodeCfaLayout,
1440 entry2.data.f);
1441 if (err == OK) {
1442 size_t listSize = builder.getSize();
1443 uint8_t opcodeListBuf[listSize];
1444 err = builder.buildOpList(opcodeListBuf);
1445 if (err == OK) {
1446 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001447 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001448 } else {
1449 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
1450 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
1451 }
1452 } else {
1453 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1454 jniThrowRuntimeException(env, "failed to add lens shading map.");
1455 }
1456 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001457 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1458 __FUNCTION__);
Ruben Brunkf967a542014-04-28 16:31:11 -07001459 }
1460 }
1461
Ruben Brunk47e91f22014-05-28 18:38:42 -07001462 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001463}
1464
1465static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1466 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001467 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001468}
1469
Ruben Brunk47e91f22014-05-28 18:38:42 -07001470static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001471 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001472
1473 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1474 if (writer == NULL) {
1475 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1476 jniThrowException(env, "java/lang/AssertionError",
1477 "setOrientation called with uninitialized DngCreator");
1478 return;
1479 }
1480
1481 uint16_t orientation = static_cast<uint16_t>(orient);
1482 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1483 TAG_ORIENTATION, writer);
1484
1485 // Set main image orientation also if in a separate IFD
1486 if (writer->hasIfd(TIFF_IFD_SUB1)) {
1487 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1488 TAG_ORIENTATION, writer);
1489 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001490}
1491
Ruben Brunk47e91f22014-05-28 18:38:42 -07001492static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001493 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001494
1495 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1496 if (writer == NULL) {
1497 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1498 jniThrowException(env, "java/lang/AssertionError",
1499 "setDescription called with uninitialized DngCreator");
1500 return;
1501 }
1502
1503 const char* desc = env->GetStringUTFChars(description, NULL);
1504 size_t len = strlen(desc) + 1;
1505
1506 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1507 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1508 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1509 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1510 }
1511
1512 env->ReleaseStringUTFChars(description, desc);
Ruben Brunkf967a542014-04-28 16:31:11 -07001513}
1514
Ruben Brunk47e91f22014-05-28 18:38:42 -07001515static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1516 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001517 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001518
1519 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1520 if (writer == NULL) {
1521 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1522 jniThrowException(env, "java/lang/AssertionError",
1523 "setGpsTags called with uninitialized DngCreator");
1524 return;
1525 }
1526
1527 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1528 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1529 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1530 TIFF_IFD_0);
1531 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1532 return;
1533 }
1534 }
1535
1536 const jsize GPS_VALUE_LENGTH = 6;
1537 jsize latLen = env->GetArrayLength(latTag);
1538 jsize longLen = env->GetArrayLength(longTag);
1539 jsize timeLen = env->GetArrayLength(timeTag);
1540 if (latLen != GPS_VALUE_LENGTH) {
1541 jniThrowException(env, "java/lang/IllegalArgumentException",
1542 "invalid latitude tag length");
1543 return;
1544 } else if (longLen != GPS_VALUE_LENGTH) {
1545 jniThrowException(env, "java/lang/IllegalArgumentException",
1546 "invalid longitude tag length");
1547 return;
1548 } else if (timeLen != GPS_VALUE_LENGTH) {
1549 jniThrowException(env, "java/lang/IllegalArgumentException",
1550 "invalid time tag length");
1551 return;
1552 }
1553
1554 uint32_t latitude[GPS_VALUE_LENGTH];
1555 uint32_t longitude[GPS_VALUE_LENGTH];
1556 uint32_t timestamp[GPS_VALUE_LENGTH];
1557
1558 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1559 reinterpret_cast<jint*>(&latitude));
1560 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1561 reinterpret_cast<jint*>(&longitude));
1562 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1563 reinterpret_cast<jint*>(&timestamp));
1564
1565 const jsize GPS_REF_LENGTH = 2;
1566 const jsize GPS_DATE_LENGTH = 11;
1567 uint8_t latitudeRef[GPS_REF_LENGTH];
1568 uint8_t longitudeRef[GPS_REF_LENGTH];
1569 uint8_t date[GPS_DATE_LENGTH];
1570
1571 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1572 latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1573 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1574 longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1575
1576 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1577 date[GPS_DATE_LENGTH - 1] = '\0';
1578
1579 {
1580 uint8_t version[] = {2, 3, 0, 0};
1581 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1582 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1583 }
1584
1585 {
1586 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1587 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1588 }
1589
1590 {
1591 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1592 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1593 }
1594
1595 {
1596 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1597 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1598 }
1599
1600 {
1601 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1602 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1603 }
1604
1605 {
1606 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1607 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1608 }
1609
1610 {
1611 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1612 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1613 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001614}
1615
Ruben Brunk47e91f22014-05-28 18:38:42 -07001616static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1617 jint height) {
1618 ALOGV("%s:", __FUNCTION__);
1619
1620 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1621 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1622 if (writer == NULL || context == NULL) {
1623 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1624 jniThrowException(env, "java/lang/AssertionError",
1625 "setThumbnail called with uninitialized DngCreator");
1626 return;
1627 }
1628
1629 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1630 jlong capacity = env->GetDirectBufferCapacity(buffer);
Andreas Gampe0f0b4912014-11-12 08:03:48 -08001631 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001632 jniThrowExceptionFmt(env, "java/lang/AssertionError",
1633 "Invalid size %d for thumbnail, expected size was %d",
1634 capacity, fullSize);
1635 return;
1636 }
1637
1638 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1639 if (pixelBytes == NULL) {
1640 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1641 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1642 return;
1643 }
1644
1645 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1646 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1647 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1648 TIFF_IFD_0);
1649 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1650 return;
1651 }
1652
1653 Vector<uint16_t> tagsToMove;
1654 tagsToMove.add(TAG_ORIENTATION);
1655 tagsToMove.add(TAG_NEWSUBFILETYPE);
1656 tagsToMove.add(TAG_BITSPERSAMPLE);
1657 tagsToMove.add(TAG_COMPRESSION);
1658 tagsToMove.add(TAG_IMAGEWIDTH);
1659 tagsToMove.add(TAG_IMAGELENGTH);
1660 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1661 tagsToMove.add(TAG_BLACKLEVEL);
1662 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1663 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1664 tagsToMove.add(TAG_PLANARCONFIGURATION);
1665 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1666 tagsToMove.add(TAG_CFAPATTERN);
1667 tagsToMove.add(TAG_CFAPLANECOLOR);
1668 tagsToMove.add(TAG_CFALAYOUT);
1669 tagsToMove.add(TAG_XRESOLUTION);
1670 tagsToMove.add(TAG_YRESOLUTION);
1671 tagsToMove.add(TAG_RESOLUTIONUNIT);
1672 tagsToMove.add(TAG_WHITELEVEL);
1673 tagsToMove.add(TAG_DEFAULTSCALE);
1674 tagsToMove.add(TAG_ROWSPERSTRIP);
1675 tagsToMove.add(TAG_STRIPBYTECOUNTS);
1676 tagsToMove.add(TAG_STRIPOFFSETS);
1677 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1678 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1679 tagsToMove.add(TAG_OPCODELIST2);
1680
1681 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1682 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1683 return;
1684 }
1685
1686 // Make sure both IFDs get the same orientation tag
1687 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1688 if (orientEntry != NULL) {
1689 writer->addEntry(orientEntry, TIFF_IFD_0);
1690 }
1691 }
1692
1693 // Setup thumbnail tags
1694
1695 {
1696 // Set photometric interpretation
1697 uint16_t interpretation = 2; // RGB
1698 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1699 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1700 }
1701
1702 {
1703 // Set planar configuration
1704 uint16_t config = 1; // Chunky
1705 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1706 env, TAG_PLANARCONFIGURATION, writer);
1707 }
1708
1709 {
1710 // Set samples per pixel
1711 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1712 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1713 env, TAG_SAMPLESPERPIXEL, writer);
1714 }
1715
1716 {
1717 // Set bits per sample
1718 uint16_t bits = BITS_PER_RGB_SAMPLE;
1719 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1720 TAG_BITSPERSAMPLE, writer);
1721 }
1722
1723 {
1724 // Set subfiletype
1725 uint32_t subfileType = 1; // Thumbnail image
1726 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1727 TAG_NEWSUBFILETYPE, writer);
1728 }
1729
1730 {
1731 // Set compression
1732 uint16_t compression = 1; // None
1733 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1734 TAG_COMPRESSION, writer);
1735 }
1736
1737 {
1738 // Set dimensions
1739 uint32_t uWidth = static_cast<uint32_t>(width);
1740 uint32_t uHeight = static_cast<uint32_t>(height);
1741 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1742 TAG_IMAGEWIDTH, writer);
1743 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1744 TAG_IMAGELENGTH, writer);
1745 }
1746
1747 {
1748 // x resolution
1749 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1750 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1751 env, TAG_XRESOLUTION, writer);
1752
1753 // y resolution
1754 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1755 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1756 env, TAG_YRESOLUTION, writer);
1757
1758 uint16_t unit = 2; // inches
1759 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1760 env, TAG_RESOLUTIONUNIT, writer);
1761 }
1762
1763 {
1764 // Setup data strips
1765 if (writer->addStrip(TIFF_IFD_0) != OK) {
1766 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1767 jniThrowException(env, "java/lang/IllegalStateException",
1768 "Failed to setup thumbnail strip tags.");
1769 return;
1770 }
1771 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1772 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1773 jniThrowException(env, "java/lang/IllegalStateException",
1774 "Failed to setup main image strip tags.");
1775 return;
1776 }
1777 }
1778
1779 if (!context->setThumbnail(pixelBytes, width, height)) {
1780 jniThrowException(env, "java/lang/IllegalStateException",
1781 "Failed to set thumbnail.");
1782 return;
1783 }
1784}
1785
1786// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07001787static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001788 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1789 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001790 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08001791 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
1792 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1793 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001794 uint32_t rStride = static_cast<uint32_t>(rowStride);
1795 uint32_t pStride = static_cast<uint32_t>(pixStride);
1796 uint32_t uWidth = static_cast<uint32_t>(width);
1797 uint32_t uHeight = static_cast<uint32_t>(height);
1798 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07001799
1800 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1801 if(env->ExceptionCheck()) {
1802 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1803 return;
1804 }
1805
Ruben Brunkf967a542014-04-28 16:31:11 -07001806 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001807 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1808 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001809 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1810 jniThrowException(env, "java/lang/AssertionError",
1811 "Write called with uninitialized DngCreator");
1812 return;
1813 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001814
1815 // Validate DNG header
1816 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001817 return;
1818 }
1819
Ruben Brunk47e91f22014-05-28 18:38:42 -07001820 sp<JniInputByteBuffer> inBuf;
1821 Vector<StripSource*> sources;
1822 sp<DirectStripSource> thumbnailSource;
1823 uint32_t targetIfd = TIFF_IFD_0;
1824
1825 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1826
1827 if (hasThumbnail) {
1828 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1829 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1830 uint32_t thumbWidth = context->getThumbnailWidth();
1831 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1832 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1833 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1834 SAMPLES_PER_RGB_PIXEL);
1835 sources.add(thumbnailSource.get());
1836 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001837 }
1838
Ruben Brunk47e91f22014-05-28 18:38:42 -07001839 if (isDirect) {
1840 size_t fullSize = rStride * uHeight;
1841 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1842 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1843 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1844 "Invalid size %d for Image, size given in metadata is %d at current stride",
1845 capacity, fullSize);
1846 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001847 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001848
Ruben Brunk47e91f22014-05-28 18:38:42 -07001849 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1850 if (pixelBytes == NULL) {
1851 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1852 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1853 return;
1854 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001855
Ruben Brunk47e91f22014-05-28 18:38:42 -07001856 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1857 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1858 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1859 sources.add(&stripSource);
1860
1861 status_t ret = OK;
1862 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1863 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001864 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001865 jniThrowExceptionFmt(env, "java/io/IOException",
1866 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001867 }
1868 return;
1869 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001870 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001871 inBuf = new JniInputByteBuffer(env, inBuffer);
1872
1873 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1874 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1875 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1876 sources.add(&stripSource);
1877
1878 status_t ret = OK;
1879 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1880 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1881 if (!env->ExceptionCheck()) {
1882 jniThrowExceptionFmt(env, "java/io/IOException",
1883 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001884 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001885 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001886 }
1887 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001888}
1889
1890static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001891 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001892 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001893
1894 uint32_t rowStride = width * BYTES_PER_SAMPLE;
1895 uint32_t pixStride = BYTES_PER_SAMPLE;
1896 uint32_t uWidth = static_cast<uint32_t>(width);
1897 uint32_t uHeight = static_cast<uint32_t>(height);
1898 uint64_t uOffset = static_cast<uint32_t>(offset);
1899
Dan Albert46d84442014-11-18 16:07:51 -08001900 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
1901 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1902 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001903
1904 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08001905 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001906 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1907 return;
1908 }
1909
1910 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1911 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1912 if (writer == NULL || context == NULL) {
1913 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1914 jniThrowException(env, "java/lang/AssertionError",
1915 "Write called with uninitialized DngCreator");
1916 return;
1917 }
1918
1919 // Validate DNG header
1920 if (!validateDngHeader(env, writer, width, height)) {
1921 return;
1922 }
1923
1924 sp<DirectStripSource> thumbnailSource;
1925 uint32_t targetIfd = TIFF_IFD_0;
1926 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1927 Vector<StripSource*> sources;
1928
1929 if (hasThumbnail) {
1930 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1931 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1932 uint32_t width = context->getThumbnailWidth();
1933 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1934 width, context->getThumbnailHeight(), bytesPerPixel,
1935 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1936 SAMPLES_PER_RGB_PIXEL);
1937 sources.add(thumbnailSource.get());
1938 targetIfd = TIFF_IFD_SUB1;
1939 }
1940
1941 sp<JniInputStream> in = new JniInputStream(env, inStream);
1942
1943 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1944 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
1945 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1946 sources.add(&stripSource);
1947
1948 status_t ret = OK;
1949 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1950 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1951 if (!env->ExceptionCheck()) {
1952 jniThrowExceptionFmt(env, "java/io/IOException",
1953 "Encountered error %d while writing file.", ret);
1954 }
1955 return;
1956 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001957}
1958
1959} /*extern "C" */
1960
1961static JNINativeMethod gDngCreatorMethods[] = {
1962 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
1963 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001964 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1965 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07001966 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
1967 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001968 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
1969 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
1970 (void*) DngCreator_nativeSetGpsTags},
1971 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
1972 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001973 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001974 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001975 (void*) DngCreator_nativeWriteInputStream},
1976};
1977
Ruben Brunkb6079002014-05-22 12:33:54 -07001978int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001979 return RegisterMethodsOrDie(env,
1980 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07001981}