blob: 7361858523cdcc731a028c027a402de795ba205f [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "DngCreator_JNI"
19
20#include <system/camera_metadata.h>
21#include <camera/CameraMetadata.h>
22#include <img_utils/DngUtils.h>
23#include <img_utils/TagDefinitions.h>
24#include <img_utils/TiffIfd.h>
25#include <img_utils/TiffWriter.h>
26#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070027#include <img_utils/Input.h>
28#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070029
30#include <utils/Log.h>
31#include <utils/Errors.h>
32#include <utils/StrongPointer.h>
33#include <utils/RefBase.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070034#include <utils/Vector.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070035#include <cutils/properties.h>
36
Ruben Brunkb8df8e02014-06-02 22:59:45 -070037#include <string.h>
Ruben Brunka3fdec82015-01-09 13:48:31 -080038#include <inttypes.h>
Ruben Brunkb8df8e02014-06-02 22:59:45 -070039
Ruben Brunkf967a542014-04-28 16:31:11 -070040#include "android_runtime/AndroidRuntime.h"
41#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
42
43#include <jni.h>
44#include <JNIHelp.h>
45
46using namespace android;
47using namespace img_utils;
48
Ruben Brunk47e91f22014-05-28 18:38:42 -070049#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070050 if ((expr) != OK) { \
51 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070052 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070053 return; \
54 }
55
Ruben Brunk47e91f22014-05-28 18:38:42 -070056#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070057 if (entry.count == 0) { \
58 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070059 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070060 return; \
61 }
62
Ruben Brunkb6079002014-05-22 12:33:54 -070063#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070064
65static struct {
66 jfieldID mNativeContext;
67} gDngCreatorClassInfo;
68
69static struct {
70 jmethodID mWriteMethod;
71} gOutputStreamClassInfo;
72
Ruben Brunk47e91f22014-05-28 18:38:42 -070073static struct {
74 jmethodID mReadMethod;
75 jmethodID mSkipMethod;
76} gInputStreamClassInfo;
77
78static struct {
79 jmethodID mGetMethod;
80} gInputByteBufferClassInfo;
81
Ruben Brunkf967a542014-04-28 16:31:11 -070082enum {
83 BITS_PER_SAMPLE = 16,
84 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -070085 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,
Ruben Brunkf967a542014-04-28 16:31:11 -070093};
94
95// ----------------------------------------------------------------------------
96
Ruben Brunk47e91f22014-05-28 18:38:42 -070097/**
98 * Container class for the persistent native context.
99 */
100
101class NativeContext : public LightRefBase<NativeContext> {
102
103public:
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
115private:
116 Vector<uint8_t> mCurrentThumbnail;
117 TiffWriter mWriter;
118 uint32_t mThumbnailWidth;
119 uint32_t mThumbnailHeight;
120};
121
122NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
123
124NativeContext::~NativeContext() {}
125
126TiffWriter* NativeContext::getWriter() {
127 return &mWriter;
128}
129
130uint32_t NativeContext::getThumbnailWidth() {
131 return mThumbnailWidth;
132}
133
134uint32_t NativeContext::getThumbnailHeight() {
135 return mThumbnailHeight;
136}
137
138const uint8_t* NativeContext::getThumbnail() {
139 return mCurrentThumbnail.array();
140}
141
142bool 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 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700165class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
166public:
167 JniOutputStream(JNIEnv* env, jobject outStream);
168
169 virtual ~JniOutputStream();
170
171 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700172
Ruben Brunkf967a542014-04-28 16:31:11 -0700173 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700174
Ruben Brunkf967a542014-04-28 16:31:11 -0700175 status_t close();
176private:
177 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700178 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700179 };
180 jobject mOutputStream;
181 JNIEnv* mEnv;
182 jbyteArray mByteArray;
183};
184
185JniOutputStream::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
193JniOutputStream::~JniOutputStream() {
194 mEnv->DeleteLocalRef(mByteArray);
195}
196
197status_t JniOutputStream::open() {
198 // Do nothing
199 return OK;
200}
201
202status_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
225status_t JniOutputStream::close() {
226 // Do nothing
227 return OK;
228}
229
Ruben Brunk47e91f22014-05-28 18:38:42 -0700230// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700231// ----------------------------------------------------------------------------
232
Ruben Brunk47e91f22014-05-28 18:38:42 -0700233/**
234 * Wrapper class for a Java InputStream.
235 *
236 * This class is not intended to be used across JNI calls.
237 */
238class JniInputStream : public Input, public LightRefBase<JniInputStream> {
239public:
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();
251private:
252 enum {
253 BYTE_ARRAY_LENGTH = 4096
254 };
255 jobject mInStream;
256 JNIEnv* mEnv;
257 jbyteArray mByteArray;
258
259};
260
261JniInputStream::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
268JniInputStream::~JniInputStream() {
269 mEnv->DeleteLocalRef(mByteArray);
270}
271
272ssize_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
296ssize_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
309status_t JniInputStream::open() {
310 // Do nothing
311 return OK;
312}
313
314status_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 */
327class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
328public:
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();
338private:
339 enum {
340 BYTE_ARRAY_LENGTH = 4096
341 };
342 jobject mInBuf;
343 JNIEnv* mEnv;
344 jbyteArray mByteArray;
345};
346
347JniInputByteBuffer::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
354JniInputByteBuffer::~JniInputByteBuffer() {
355 mEnv->DeleteLocalRef(mByteArray);
356}
357
358ssize_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
Ruben Brunka3fdec82015-01-09 13:48:31 -0800364 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700365 realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800366 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700367
368 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800369 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700370 return BAD_VALUE;
371 }
372
373 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
374 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800375 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700376 return BAD_VALUE;
377 }
378 return realCount;
379}
380
381status_t JniInputByteBuffer::open() {
382 // Do nothing
383 return OK;
384}
385
386status_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
400class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
401public:
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;
411protected:
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
424InputStripSource::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
431InputStripSource::~InputStripSource() {}
432
433status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
434 status_t err = OK;
Ruben Brunk3e190252014-08-12 11:09:01 -0700435 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700436 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;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800476 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700477
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) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800482 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
483 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700484 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800485 "Early EOF encountered, not enough pixel data for image of size %"
486 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700487 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
522uint32_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
535class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
536public:
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;
546protected:
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
559DirectStripSource::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
566DirectStripSource::~DirectStripSource() {}
567
568status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700569 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700570
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
613uint32_t DirectStripSource::getIfd() const {
614 return mIfd;
615}
616
617// End of DirectStripSource
618// ----------------------------------------------------------------------------
619
620static 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
642static 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
Ruben Brunkd70132c2014-08-22 16:24:49 -0700662/**
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 */
666static 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 */
710static 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 */
749static 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
Ruben Brunk47e91f22014-05-28 18:38:42 -0700775// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700776extern "C" {
777
Ruben Brunk47e91f22014-05-28 18:38:42 -0700778static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700779 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700780 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700781 gDngCreatorClassInfo.mNativeContext));
782}
783
Ruben Brunk47e91f22014-05-28 18:38:42 -0700784static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700785 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700786 NativeContext* current = DngCreator_getNativeContext(env, thiz);
787
788 if (context != NULL) {
789 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700790 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700791
Ruben Brunkf967a542014-04-28 16:31:11 -0700792 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700793 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700794 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700795
Ruben Brunkf967a542014-04-28 16:31:11 -0700796 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700797 reinterpret_cast<jlong>(context.get()));
798}
799
800static 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;
Ruben Brunkf967a542014-04-28 16:31:11 -0700807}
808
809static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
810 ALOGV("%s:", __FUNCTION__);
811
812 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
Ruben Brunkb6079002014-05-22 12:33:54 -0700813 ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700814 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
Ruben Brunkb6079002014-05-22 12:33:54 -0700815 "can't find android/hardware/camera2/DngCreator.%s",
816 ANDROID_DNGCREATOR_CTX_JNI_ID);
Ruben Brunkf967a542014-04-28 16:31:11 -0700817
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");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700822
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");
Ruben Brunkf967a542014-04-28 16:31:11 -0700835}
836
837static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700838 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700839 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
Ruben Brunk47e91f22014-05-28 18:38:42 -0700853 sp<NativeContext> nativeContext = new NativeContext();
854 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700855
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;
Ruben Brunkd70132c2014-08-22 16:24:49 -0700867 uint8_t cfaPlaneColor[3] = {0, 1, 2};
868 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -0700869
870 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700871 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -0700872
873 // Setup main image tags
874
Ruben Brunkf967a542014-04-28 16:31:11 -0700875 {
876 // Set orientation
877 uint16_t orientation = 1; // Normal
878 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700879 TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700880 }
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700886 TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700887 }
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700893 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700894 }
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700900 TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700901 }
902
903 {
904 // Set dimensions
905 camera_metadata_entry entry =
906 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700907 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700908 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700911 TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700912 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700913 TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700914 imageWidth = width;
915 imageHeight = height;
916 }
917
918 {
919 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -0700920 uint16_t interpretation = 32803; // CFA
Ruben Brunkf967a542014-04-28 16:31:11 -0700921 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700922 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700923 }
924
925 {
926 // Set blacklevel tags
927 camera_metadata_entry entry =
928 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700929 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700930 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700932 TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700933
934 uint16_t repeatDim[2] = {2, 2};
935 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700936 TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700937 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700943 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700944 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700950 env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700951 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700957 env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700958 }
959
960 {
961 // Set CFA pattern
962 camera_metadata_entry entry =
963 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700964 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700965
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);
Ruben Brunkf967a542014-04-28 16:31:11 -0700972 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700973
974 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
975 TAG_CFAPATTERN, writer);
976
977 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -0700978 }
979
980 {
981 // Set CFA plane color
Ruben Brunkf967a542014-04-28 16:31:11 -0700982 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700983 env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700984 }
985
986 {
987 // Set CFA layout
988 uint16_t cfaLayout = 1;
989 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700990 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700991 }
992
993 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700994 // image description
995 uint8_t imageDescription = '\0'; // empty
996 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700997 env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700998 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001009 TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001010 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001021 TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001022 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001028 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001029
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001033 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001034
1035 uint16_t unit = 2; // inches
1036 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001037 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001038 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001046 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001047 }
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
Ruben Brunk47e91f22014-05-28 18:38:42 -07001061 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 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001068
1069 // datetime original
Ruben Brunk47e91f22014-05-28 18:38:42 -07001070 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 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001077 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001084 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001085 }
1086
1087 {
1088 // copyright
1089 uint8_t copyright = '\0'; // empty
1090 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001091 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001092 }
1093
1094 {
1095 // exposure time
1096 camera_metadata_entry entry =
1097 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001098 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001099
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001124 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001125
1126 }
1127
1128 {
1129 // ISO speed ratings
1130 camera_metadata_entry entry =
1131 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001132 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001133
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001148 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001149 }
1150
1151 {
1152 // focal length
1153 camera_metadata_entry entry =
1154 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001155 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001156
1157 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1158 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001159 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001160 }
1161
1162 {
1163 // f number
1164 camera_metadata_entry entry =
1165 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001166 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001167
1168 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1169 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001170 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001171 }
1172
1173 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001174 // 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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001177 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001178
1179 uint8_t backwardVersion[4] = {1, 1, 0, 0};
1180 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001181 env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001182 }
1183
1184 {
1185 // Set whitelevel
1186 camera_metadata_entry entry =
1187 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001188 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001189 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001191 TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001192 }
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),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001198 env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001199 }
1200
1201 bool singleIlluminant = false;
1202 {
1203 // Set calibration illuminants
1204 camera_metadata_entry entry1 =
1205 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001206 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001207 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001215 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001216
1217 if (!singleIlluminant) {
1218 uint16_t ref2 = entry2.data.u8[0];
1219 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001220 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001221 }
1222 }
1223
1224 {
1225 // Set color transforms
1226 camera_metadata_entry entry1 =
1227 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001228 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001229
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
Ruben Brunk47e91f22014-05-28 18:38:42 -07001238 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1239 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001240
1241 if (!singleIlluminant) {
1242 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001243 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001244 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
Ruben Brunk47e91f22014-05-28 18:38:42 -07001252 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1253 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001254 }
1255 }
1256
1257 {
1258 // Set calibration transforms
1259 camera_metadata_entry entry1 =
1260 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001261 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001262
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
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001271 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1272 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001273
1274 if (!singleIlluminant) {
1275 camera_metadata_entry entry2 =
1276 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001277 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001278 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
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001286 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe9592b012014-11-11 00:23:02 -08001287 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001288 }
1289 }
1290
1291 {
1292 // Set forward transforms
1293 camera_metadata_entry entry1 =
1294 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001295 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001296
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001306 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001307
1308 if (!singleIlluminant) {
1309 camera_metadata_entry entry2 =
1310 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001311 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001312 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001321 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001322 }
1323 }
1324
1325 {
1326 // Set camera neutral
1327 camera_metadata_entry entry =
1328 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001329 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001330 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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001341 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001342 }
1343
1344 {
1345 // Setup data strips
1346 // TODO: Switch to tiled implementation.
Ruben Brunk47e91f22014-05-28 18:38:42 -07001347 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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001353 }
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001363 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001364 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001365 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001366 }
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001388 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001389 }
1390
1391 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001392 // Setup sensor noise model
1393 camera_metadata_entry entry =
1394 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1395
Ruben Brunkd70132c2014-08-22 16:24:49 -07001396 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
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001408 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001409 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 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001423 } else {
1424 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1425 __FUNCTION__);
1426 }
1427 }
1428
1429 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001430 // Setup opcode List 2
1431 camera_metadata_entry entry1 =
1432 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001433
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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001441
1442 camera_metadata_entry entry2 =
1443 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001444
1445 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001446
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,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001462 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001463 } 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 {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001472 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1473 __FUNCTION__);
Ruben Brunkf967a542014-04-28 16:31:11 -07001474 }
1475 }
1476
Ruben Brunk47e91f22014-05-28 18:38:42 -07001477 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001478}
1479
1480static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1481 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001482 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001483}
1484
Ruben Brunk47e91f22014-05-28 18:38:42 -07001485static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001486 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001487
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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001505}
1506
Ruben Brunk47e91f22014-05-28 18:38:42 -07001507static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001508 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001509
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);
Ruben Brunkf967a542014-04-28 16:31:11 -07001528}
1529
Ruben Brunk47e91f22014-05-28 18:38:42 -07001530static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1531 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001532 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001533
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*>(&timestamp));
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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001629}
1630
Ruben Brunk47e91f22014-05-28 18:38:42 -07001631static 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.
Ruben Brunkf967a542014-04-28 16:31:11 -07001802static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001803 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1804 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001805 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001806 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);
Ruben Brunkf967a542014-04-28 16:31:11 -07001813
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
Ruben Brunkf967a542014-04-28 16:31:11 -07001820 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001821 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1822 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001823 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1824 jniThrowException(env, "java/lang/AssertionError",
1825 "Write called with uninitialized DngCreator");
1826 return;
1827 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001828
1829 // Validate DNG header
1830 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001831 return;
1832 }
1833
Ruben Brunk47e91f22014-05-28 18:38:42 -07001834 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;
Ruben Brunkf967a542014-04-28 16:31:11 -07001851 }
1852
Ruben Brunk47e91f22014-05-28 18:38:42 -07001853 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;
Ruben Brunkf967a542014-04-28 16:31:11 -07001861 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001862
Ruben Brunk47e91f22014-05-28 18:38:42 -07001863 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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001869
Ruben Brunk47e91f22014-05-28 18:38:42 -07001870 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);
Ruben Brunkf967a542014-04-28 16:31:11 -07001878 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001879 jniThrowExceptionFmt(env, "java/io/IOException",
1880 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001881 }
1882 return;
1883 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001884 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001885 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);
Ruben Brunkf967a542014-04-28 16:31:11 -07001898 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001899 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001900 }
1901 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001902}
1903
1904static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001905 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001906 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001907
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 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001971}
1972
1973} /*extern "C" */
1974
1975static JNINativeMethod gDngCreatorMethods[] = {
1976 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
1977 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001978 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1979 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07001980 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
1981 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001982 {"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",
Ruben Brunkf967a542014-04-28 16:31:11 -07001987 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001988 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001989 (void*) DngCreator_nativeWriteInputStream},
1990};
1991
Ruben Brunkb6079002014-05-22 12:33:54 -07001992int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001993 return AndroidRuntime::registerNativeMethods(env,
Ruben Brunkb6079002014-05-22 12:33:54 -07001994 "android/hardware/camera2/DngCreator", gDngCreatorMethods,
1995 NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07001996}