blob: 7d8d151df0d2e2ce98ce503bd9e45323a3775dc9 [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 <utils/Log.h>
41#include <utils/Errors.h>
42#include <utils/StrongPointer.h>
43#include <utils/RefBase.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070044#include <utils/Vector.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070045#include <cutils/properties.h>
46
Ruben Brunkb8df8e02014-06-02 22:59:45 -070047#include <string.h>
Ruben Brunka3fdec82015-01-09 13:48:31 -080048#include <inttypes.h>
Ruben Brunkb8df8e02014-06-02 22:59:45 -070049
Ruben Brunkf967a542014-04-28 16:31:11 -070050#include "android_runtime/AndroidRuntime.h"
51#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
52
53#include <jni.h>
54#include <JNIHelp.h>
55
56using namespace android;
57using namespace img_utils;
58
Ruben Brunk47e91f22014-05-28 18:38:42 -070059#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070060 if ((expr) != OK) { \
61 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070062 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070063 return; \
64 }
65
Ruben Brunk47e91f22014-05-28 18:38:42 -070066#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070067 if (entry.count == 0) { \
68 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070069 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070070 return; \
71 }
72
Ruben Brunkb6079002014-05-22 12:33:54 -070073#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070074
75static struct {
76 jfieldID mNativeContext;
77} gDngCreatorClassInfo;
78
79static struct {
80 jmethodID mWriteMethod;
81} gOutputStreamClassInfo;
82
Ruben Brunk47e91f22014-05-28 18:38:42 -070083static struct {
84 jmethodID mReadMethod;
85 jmethodID mSkipMethod;
86} gInputStreamClassInfo;
87
88static struct {
89 jmethodID mGetMethod;
90} gInputByteBufferClassInfo;
91
Ruben Brunkf967a542014-04-28 16:31:11 -070092enum {
93 BITS_PER_SAMPLE = 16,
94 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -070095 BYTES_PER_RGB_PIXEL = 3,
96 BITS_PER_RGB_SAMPLE = 8,
97 BYTES_PER_RGB_SAMPLE = 1,
98 SAMPLES_PER_RGB_PIXEL = 3,
99 SAMPLES_PER_RAW_PIXEL = 1,
100 TIFF_IFD_0 = 0,
101 TIFF_IFD_SUB1 = 1,
102 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -0700103};
104
105// ----------------------------------------------------------------------------
106
Ruben Brunk47e91f22014-05-28 18:38:42 -0700107/**
108 * Container class for the persistent native context.
109 */
110
111class NativeContext : public LightRefBase<NativeContext> {
112
113public:
114 NativeContext();
115 virtual ~NativeContext();
116
117 TiffWriter* getWriter();
118
119 uint32_t getThumbnailWidth();
120 uint32_t getThumbnailHeight();
121 const uint8_t* getThumbnail();
122
123 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
124
125private:
126 Vector<uint8_t> mCurrentThumbnail;
127 TiffWriter mWriter;
128 uint32_t mThumbnailWidth;
129 uint32_t mThumbnailHeight;
130};
131
132NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
133
134NativeContext::~NativeContext() {}
135
136TiffWriter* NativeContext::getWriter() {
137 return &mWriter;
138}
139
140uint32_t NativeContext::getThumbnailWidth() {
141 return mThumbnailWidth;
142}
143
144uint32_t NativeContext::getThumbnailHeight() {
145 return mThumbnailHeight;
146}
147
148const uint8_t* NativeContext::getThumbnail() {
149 return mCurrentThumbnail.array();
150}
151
152bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
153 mThumbnailWidth = width;
154 mThumbnailHeight = height;
155
156 size_t size = BYTES_PER_RGB_PIXEL * width * height;
157 if (mCurrentThumbnail.resize(size) < 0) {
158 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
159 return false;
160 }
161
162 uint8_t* thumb = mCurrentThumbnail.editArray();
163 memcpy(thumb, buffer, size);
164 return true;
165}
166
167// End of NativeContext
168// ----------------------------------------------------------------------------
169
170/**
171 * Wrapper class for a Java OutputStream.
172 *
173 * This class is not intended to be used across JNI calls.
174 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700175class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
176public:
177 JniOutputStream(JNIEnv* env, jobject outStream);
178
179 virtual ~JniOutputStream();
180
181 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700182
Ruben Brunkf967a542014-04-28 16:31:11 -0700183 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700184
Ruben Brunkf967a542014-04-28 16:31:11 -0700185 status_t close();
186private:
187 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700188 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700189 };
190 jobject mOutputStream;
191 JNIEnv* mEnv;
192 jbyteArray mByteArray;
193};
194
195JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
196 mEnv(env) {
197 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
198 if (mByteArray == NULL) {
199 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
200 }
201}
202
203JniOutputStream::~JniOutputStream() {
204 mEnv->DeleteLocalRef(mByteArray);
205}
206
207status_t JniOutputStream::open() {
208 // Do nothing
209 return OK;
210}
211
212status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
213 while(count > 0) {
214 size_t len = BYTE_ARRAY_LENGTH;
215 len = (count > len) ? len : count;
216 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
217
218 if (mEnv->ExceptionCheck()) {
219 return BAD_VALUE;
220 }
221
222 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
223 0, len);
224
225 if (mEnv->ExceptionCheck()) {
226 return BAD_VALUE;
227 }
228
229 count -= len;
230 offset += len;
231 }
232 return OK;
233}
234
235status_t JniOutputStream::close() {
236 // Do nothing
237 return OK;
238}
239
Ruben Brunk47e91f22014-05-28 18:38:42 -0700240// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700241// ----------------------------------------------------------------------------
242
Ruben Brunk47e91f22014-05-28 18:38:42 -0700243/**
244 * Wrapper class for a Java InputStream.
245 *
246 * This class is not intended to be used across JNI calls.
247 */
248class JniInputStream : public Input, public LightRefBase<JniInputStream> {
249public:
250 JniInputStream(JNIEnv* env, jobject inStream);
251
252 status_t open();
253
254 status_t close();
255
256 ssize_t read(uint8_t* buf, size_t offset, size_t count);
257
258 ssize_t skip(size_t count);
259
260 virtual ~JniInputStream();
261private:
262 enum {
263 BYTE_ARRAY_LENGTH = 4096
264 };
265 jobject mInStream;
266 JNIEnv* mEnv;
267 jbyteArray mByteArray;
268
269};
270
271JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
272 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
273 if (mByteArray == NULL) {
274 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
275 }
276}
277
278JniInputStream::~JniInputStream() {
279 mEnv->DeleteLocalRef(mByteArray);
280}
281
282ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
283
284 jint realCount = BYTE_ARRAY_LENGTH;
285 if (count < BYTE_ARRAY_LENGTH) {
286 realCount = count;
287 }
288 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
289 realCount);
290
291 if (actual < 0) {
292 return NOT_ENOUGH_DATA;
293 }
294
295 if (mEnv->ExceptionCheck()) {
296 return BAD_VALUE;
297 }
298
299 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
300 if (mEnv->ExceptionCheck()) {
301 return BAD_VALUE;
302 }
303 return actual;
304}
305
306ssize_t JniInputStream::skip(size_t count) {
307 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
308 static_cast<jlong>(count));
309
310 if (mEnv->ExceptionCheck()) {
311 return BAD_VALUE;
312 }
313 if (actual < 0) {
314 return NOT_ENOUGH_DATA;
315 }
316 return actual;
317}
318
319status_t JniInputStream::open() {
320 // Do nothing
321 return OK;
322}
323
324status_t JniInputStream::close() {
325 // Do nothing
326 return OK;
327}
328
329// End of JniInputStream
330// ----------------------------------------------------------------------------
331
332/**
333 * Wrapper class for a non-direct Java ByteBuffer.
334 *
335 * This class is not intended to be used across JNI calls.
336 */
337class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
338public:
339 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
340
341 status_t open();
342
343 status_t close();
344
345 ssize_t read(uint8_t* buf, size_t offset, size_t count);
346
347 virtual ~JniInputByteBuffer();
348private:
349 enum {
350 BYTE_ARRAY_LENGTH = 4096
351 };
352 jobject mInBuf;
353 JNIEnv* mEnv;
354 jbyteArray mByteArray;
355};
356
357JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
358 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
359 if (mByteArray == NULL) {
360 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
361 }
362}
363
364JniInputByteBuffer::~JniInputByteBuffer() {
365 mEnv->DeleteLocalRef(mByteArray);
366}
367
368ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
369 jint realCount = BYTE_ARRAY_LENGTH;
370 if (count < BYTE_ARRAY_LENGTH) {
371 realCount = count;
372 }
373
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700374 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod,
375 mByteArray, 0, realCount);
Ruben Brunka3fdec82015-01-09 13:48:31 -0800376 mEnv->DeleteLocalRef(chainingBuf);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700377
378 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800379 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700380 return BAD_VALUE;
381 }
382
383 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
384 if (mEnv->ExceptionCheck()) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800385 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700386 return BAD_VALUE;
387 }
388 return realCount;
389}
390
391status_t JniInputByteBuffer::open() {
392 // Do nothing
393 return OK;
394}
395
396status_t JniInputByteBuffer::close() {
397 // Do nothing
398 return OK;
399}
400
401// End of JniInputByteBuffer
402// ----------------------------------------------------------------------------
403
404/**
405 * StripSource subclass for Input types.
406 *
407 * This class is not intended to be used across JNI calls.
408 */
409
410class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
411public:
412 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
413 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
414 uint32_t samplesPerPixel);
415
416 virtual ~InputStripSource();
417
418 virtual status_t writeToStream(Output& stream, uint32_t count);
419
420 virtual uint32_t getIfd() const;
421protected:
422 uint32_t mIfd;
423 Input* mInput;
424 uint32_t mWidth;
425 uint32_t mHeight;
426 uint32_t mPixStride;
427 uint32_t mRowStride;
428 uint64_t mOffset;
429 JNIEnv* mEnv;
430 uint32_t mBytesPerSample;
431 uint32_t mSamplesPerPixel;
432};
433
434InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
435 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
436 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
437 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
438 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
439 mSamplesPerPixel(samplesPerPixel) {}
440
441InputStripSource::~InputStripSource() {}
442
443status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700444 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700445 jlong offset = mOffset;
446
447 if (fullSize != count) {
448 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
449 fullSize);
450 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
451 return BAD_VALUE;
452 }
453
454 // Skip offset
455 while (offset > 0) {
456 ssize_t skipped = mInput->skip(offset);
457 if (skipped <= 0) {
458 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
459 jniThrowExceptionFmt(mEnv, "java/io/IOException",
460 "Early EOF encountered in skip, not enough pixel data for image of size %u",
461 fullSize);
462 skipped = NOT_ENOUGH_DATA;
463 } else {
464 if (!mEnv->ExceptionCheck()) {
465 jniThrowException(mEnv, "java/io/IOException",
466 "Error encountered while skip bytes in input stream.");
467 }
468 }
469
470 return skipped;
471 }
472 offset -= skipped;
473 }
474
475 Vector<uint8_t> row;
476 if (row.resize(mRowStride) < 0) {
477 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
478 return BAD_VALUE;
479 }
480
481 uint8_t* rowBytes = row.editArray();
482
483 for (uint32_t i = 0; i < mHeight; ++i) {
484 size_t rowFillAmt = 0;
Ruben Brunka3fdec82015-01-09 13:48:31 -0800485 size_t rowSize = mRowStride;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700486
487 while (rowFillAmt < mRowStride) {
488 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
489 if (bytesRead <= 0) {
490 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
Ruben Brunka3fdec82015-01-09 13:48:31 -0800491 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd",
492 __FUNCTION__, i, bytesRead);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700493 jniThrowExceptionFmt(mEnv, "java/io/IOException",
Ruben Brunka3fdec82015-01-09 13:48:31 -0800494 "Early EOF encountered, not enough pixel data for image of size %"
495 PRIu32, fullSize);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700496 bytesRead = NOT_ENOUGH_DATA;
497 } else {
498 if (!mEnv->ExceptionCheck()) {
499 jniThrowException(mEnv, "java/io/IOException",
500 "Error encountered while reading");
501 }
502 }
503 return bytesRead;
504 }
505 rowFillAmt += bytesRead;
506 rowSize -= bytesRead;
507 }
508
509 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
510 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
511
512 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
513 mEnv->ExceptionCheck()) {
514 if (!mEnv->ExceptionCheck()) {
515 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
516 }
517 return BAD_VALUE;
518 }
519 } else {
520 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
521 jniThrowException(mEnv, "java/lang/IllegalStateException",
522 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
523 return BAD_VALUE;
524
525 // TODO: Add support for non-contiguous pixels if needed.
526 }
527 }
528 return OK;
529}
530
531uint32_t InputStripSource::getIfd() const {
532 return mIfd;
533}
534
535// End of InputStripSource
536// ----------------------------------------------------------------------------
537
538/**
539 * StripSource subclass for direct buffer types.
540 *
541 * This class is not intended to be used across JNI calls.
542 */
543
544class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
545public:
546 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
547 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
548 uint32_t bytesPerSample, uint32_t samplesPerPixel);
549
550 virtual ~DirectStripSource();
551
552 virtual status_t writeToStream(Output& stream, uint32_t count);
553
554 virtual uint32_t getIfd() const;
555protected:
556 uint32_t mIfd;
557 const uint8_t* mPixelBytes;
558 uint32_t mWidth;
559 uint32_t mHeight;
560 uint32_t mPixStride;
561 uint32_t mRowStride;
562 uint16_t mOffset;
563 JNIEnv* mEnv;
564 uint32_t mBytesPerSample;
565 uint32_t mSamplesPerPixel;
566};
567
568DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
569 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
570 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
571 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
572 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
573 mSamplesPerPixel(samplesPerPixel) {}
574
575DirectStripSource::~DirectStripSource() {}
576
577status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
Ruben Brunk3e190252014-08-12 11:09:01 -0700578 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel;
Ruben Brunk47e91f22014-05-28 18:38:42 -0700579
580 if (fullSize != count) {
581 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
582 fullSize);
583 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
584 return BAD_VALUE;
585 }
586
587 if (mPixStride == mBytesPerSample * mSamplesPerPixel
588 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
589 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
590
591 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
592 if (!mEnv->ExceptionCheck()) {
593 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
594 }
595 return BAD_VALUE;
596 }
597 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
598 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
599
600 for (size_t i = 0; i < mHeight; ++i) {
601 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
602 mEnv->ExceptionCheck()) {
603 if (!mEnv->ExceptionCheck()) {
604 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
605 }
606 return BAD_VALUE;
607 }
608 }
609 } else {
610 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
611
612 jniThrowException(mEnv, "java/lang/IllegalStateException",
613 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
614 return BAD_VALUE;
615
616 // TODO: Add support for non-contiguous pixels if needed.
617 }
618 return OK;
619
620}
621
622uint32_t DirectStripSource::getIfd() const {
623 return mIfd;
624}
625
626// End of DirectStripSource
627// ----------------------------------------------------------------------------
628
629static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
630 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
631
632 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700633 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 :
634 TIFF_IFD_0)->getData<uint32_t>());
635 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 :
636 TIFF_IFD_0)->getData<uint32_t>());
Ruben Brunk47e91f22014-05-28 18:38:42 -0700637
638 if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
639 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
640 "Metadata width %d doesn't match image width %d", metadataWidth, width);
641 return false;
642 }
643
644 if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
645 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
Ruben Brunk5f2368d2015-06-05 17:03:49 -0700646 "Metadata height %d doesn't match image height %d",
647 metadataHeight, height);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700648 return false;
649 }
650
651 return true;
652}
653
654static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
655 const Vector<uint16_t>& entries) {
656 for (size_t i = 0; i < entries.size(); ++i) {
657 uint16_t tagId = entries[i];
658 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
659 if (entry == NULL) {
660 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
661 ifdFrom);
662 return BAD_VALUE;
663 }
664 if (writer->addEntry(entry, ifdTo) != OK) {
665 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
666 ifdFrom);
667 return BAD_VALUE;
668 }
669 writer->removeEntry(tagId, ifdFrom);
670 }
671 return OK;
672}
673
Ruben Brunkd70132c2014-08-22 16:24:49 -0700674/**
675 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
676 * Returns OK on success, or a negative error code if the CFA enum was invalid.
677 */
678static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
679 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
680 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
681 cfaEnum);
682 switch(cfa) {
683 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
684 cfaOut[0] = 0;
685 cfaOut[1] = 1;
686 cfaOut[2] = 1;
687 cfaOut[3] = 2;
688 break;
689 }
690 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
691 cfaOut[0] = 1;
692 cfaOut[1] = 0;
693 cfaOut[2] = 2;
694 cfaOut[3] = 1;
695 break;
696 }
697 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
698 cfaOut[0] = 1;
699 cfaOut[1] = 2;
700 cfaOut[2] = 0;
701 cfaOut[3] = 1;
702 break;
703 }
704 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
705 cfaOut[0] = 2;
706 cfaOut[1] = 1;
707 cfaOut[2] = 1;
708 cfaOut[3] = 0;
709 break;
710 }
711 default: {
712 return BAD_VALUE;
713 }
714 }
715 return OK;
716}
717
718/**
719 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
720 * RGGB for an unknown enum.
721 */
722static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
723 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
724 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
725 cfaEnum);
726 switch(cfa) {
727 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
728 return OpcodeListBuilder::CFA_RGGB;
729 }
730 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
731 return OpcodeListBuilder::CFA_GRBG;
732 }
733 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
734 return OpcodeListBuilder::CFA_GBRG;
735 }
736 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
737 return OpcodeListBuilder::CFA_BGGR;
738 }
739 default: {
740 return OpcodeListBuilder::CFA_RGGB;
741 }
742 }
743}
744
745/**
746 * For each color plane, find the corresponding noise profile coefficients given in the
747 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
748 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
749 *
750 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
751 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
752 * coefficients.
753 * numChannels - the number of noise profile coefficient pairs and color channels given in
754 * the perChannelNoiseProfile and cfa arguments, respectively.
755 * planeColors - the color planes in the noise profile output.
756 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
757 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
758 *
759 * returns OK, or a negative error code on failure.
760 */
761static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
762 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
763 /*out*/double* noiseProfile) {
764
765 for (size_t p = 0; p < numPlanes; ++p) {
766 size_t S = p * 2;
767 size_t O = p * 2 + 1;
768
769 noiseProfile[S] = 0;
770 noiseProfile[O] = 0;
771 bool uninitialized = true;
772 for (size_t c = 0; c < numChannels; ++c) {
773 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
774 noiseProfile[S] = perChannelNoiseProfile[c * 2];
775 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
776 uninitialized = false;
777 }
778 }
779 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800780 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
781 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700782 return BAD_VALUE;
783 }
784 }
785 return OK;
786}
787
Ruben Brunk47e91f22014-05-28 18:38:42 -0700788// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700789extern "C" {
790
Ruben Brunk47e91f22014-05-28 18:38:42 -0700791static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700792 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700793 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700794 gDngCreatorClassInfo.mNativeContext));
795}
796
Ruben Brunk47e91f22014-05-28 18:38:42 -0700797static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700798 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700799 NativeContext* current = DngCreator_getNativeContext(env, thiz);
800
801 if (context != NULL) {
802 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700803 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700804
Ruben Brunkf967a542014-04-28 16:31:11 -0700805 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700806 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700807 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700808
Ruben Brunkf967a542014-04-28 16:31:11 -0700809 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700810 reinterpret_cast<jlong>(context.get()));
811}
812
813static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
814 ALOGV("%s:", __FUNCTION__);
815 NativeContext* current = DngCreator_getNativeContext(env, thiz);
816 if (current) {
817 return current->getWriter();
818 }
819 return NULL;
Ruben Brunkf967a542014-04-28 16:31:11 -0700820}
821
822static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
823 ALOGV("%s:", __FUNCTION__);
824
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800825 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
826 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700827
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800828 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
829 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
830 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700831
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800832 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
833 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
834 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700835
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800836 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
837 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
838 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -0700839}
840
841static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700842 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700843 ALOGV("%s:", __FUNCTION__);
844 CameraMetadata characteristics;
845 CameraMetadata results;
846 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
847 jniThrowException(env, "java/lang/AssertionError",
848 "No native metadata defined for camera characteristics.");
849 return;
850 }
851 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
852 jniThrowException(env, "java/lang/AssertionError",
853 "No native metadata defined for capture results.");
854 return;
855 }
856
Ruben Brunk47e91f22014-05-28 18:38:42 -0700857 sp<NativeContext> nativeContext = new NativeContext();
858 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700859
860 writer->addIfd(TIFF_IFD_0);
861
862 status_t err = OK;
863
864 const uint32_t samplesPerPixel = 1;
865 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -0700866 uint32_t imageWidth = 0;
867 uint32_t imageHeight = 0;
868
869 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
Ruben Brunkd70132c2014-08-22 16:24:49 -0700870 uint8_t cfaPlaneColor[3] = {0, 1, 2};
871 uint8_t cfaEnum = -1;
Ruben Brunkf967a542014-04-28 16:31:11 -0700872
873 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700874 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -0700875
876 // Setup main image tags
877
Ruben Brunkf967a542014-04-28 16:31:11 -0700878 {
879 // Set orientation
880 uint16_t orientation = 1; // Normal
881 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700882 TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700883 }
884
885 {
886 // Set subfiletype
887 uint32_t subfileType = 0; // Main image
888 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700889 TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700890 }
891
892 {
893 // Set bits per sample
894 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
895 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700896 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700897 }
898
899 {
900 // Set compression
901 uint16_t compression = 1; // None
902 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700903 TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700904 }
905
906 {
907 // Set dimensions
908 camera_metadata_entry entry =
909 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700910 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700911 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
912 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
913 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700914 TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700915 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700916 TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700917 imageWidth = width;
918 imageHeight = height;
919 }
920
921 {
922 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -0700923 uint16_t interpretation = 32803; // CFA
Ruben Brunkf967a542014-04-28 16:31:11 -0700924 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700925 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700926 }
927
928 {
929 // Set blacklevel tags
930 camera_metadata_entry entry =
931 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700932 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700933 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
934 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700935 TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700936
937 uint16_t repeatDim[2] = {2, 2};
938 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700939 TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700940 }
941
942 {
943 // Set samples per pixel
944 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
945 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700946 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700947 }
948
949 {
950 // Set planar configuration
951 uint16_t config = 1; // Chunky
952 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700953 env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700954 }
955
956 {
957 // Set CFA pattern dimensions
958 uint16_t repeatDim[2] = {2, 2};
959 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700960 env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700961 }
962
963 {
964 // Set CFA pattern
965 camera_metadata_entry entry =
966 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700967 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700968
969 const int cfaLength = 4;
970 cfaEnum = entry.data.u8[0];
971 uint8_t cfa[cfaLength];
972 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
973 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
974 "Invalid metadata for tag %d", TAG_CFAPATTERN);
Ruben Brunkf967a542014-04-28 16:31:11 -0700975 }
Ruben Brunkd70132c2014-08-22 16:24:49 -0700976
977 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
978 TAG_CFAPATTERN, writer);
979
980 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
Ruben Brunkf967a542014-04-28 16:31:11 -0700981 }
982
983 {
984 // Set CFA plane color
Ruben Brunkf967a542014-04-28 16:31:11 -0700985 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700986 env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700987 }
988
989 {
990 // Set CFA layout
991 uint16_t cfaLayout = 1;
992 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700993 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700994 }
995
996 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700997 // image description
998 uint8_t imageDescription = '\0'; // empty
999 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001000 env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001001 }
1002
1003 {
1004 // make
1005 char manufacturer[PROPERTY_VALUE_MAX];
1006
1007 // Use "" to represent unknown make as suggested in TIFF/EP spec.
1008 property_get("ro.product.manufacturer", manufacturer, "");
1009 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
1010
1011 BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001012 TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001013 }
1014
1015 {
1016 // model
1017 char model[PROPERTY_VALUE_MAX];
1018
1019 // Use "" to represent unknown model as suggested in TIFF/EP spec.
1020 property_get("ro.product.model", model, "");
1021 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
1022
1023 BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001024 TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001025 }
1026
1027 {
1028 // x resolution
1029 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1030 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001031 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001032
1033 // y resolution
1034 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1035 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001036 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001037
1038 uint16_t unit = 2; // inches
1039 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001040 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001041 }
1042
1043 {
1044 // software
1045 char software[PROPERTY_VALUE_MAX];
1046 property_get("ro.build.fingerprint", software, "");
1047 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
1048 BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001049 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001050 }
1051
1052 {
1053 // datetime
1054 const size_t DATETIME_COUNT = 20;
1055 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
1056
1057 size_t len = strlen(captureTime) + 1;
1058 if (len != DATETIME_COUNT) {
1059 jniThrowException(env, "java/lang/IllegalArgumentException",
1060 "Timestamp string length is not required 20 characters");
1061 return;
1062 }
1063
Ruben Brunk47e91f22014-05-28 18:38:42 -07001064 if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
1065 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1066 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1067 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1068 "Invalid metadata for tag %x", TAG_DATETIME);
1069 return;
1070 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001071
1072 // datetime original
Ruben Brunk47e91f22014-05-28 18:38:42 -07001073 if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
1074 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
1075 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1076 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1077 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
1078 return;
1079 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001080 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
1081 }
1082
1083 {
1084 // TIFF/EP standard id
1085 uint8_t standardId[] = { 1, 0, 0, 0 };
1086 BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001087 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001088 }
1089
1090 {
1091 // copyright
1092 uint8_t copyright = '\0'; // empty
1093 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001094 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001095 }
1096
1097 {
1098 // exposure time
1099 camera_metadata_entry entry =
1100 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001101 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001102
1103 int64_t exposureTime = *(entry.data.i64);
1104
1105 if (exposureTime < 0) {
1106 // Should be unreachable
1107 jniThrowException(env, "java/lang/IllegalArgumentException",
1108 "Negative exposure time in metadata");
1109 return;
1110 }
1111
1112 // Ensure exposure time doesn't overflow (for exposures > 4s)
1113 uint32_t denominator = 1000000000;
1114 while (exposureTime > UINT32_MAX) {
1115 exposureTime >>= 1;
1116 denominator >>= 1;
1117 if (denominator == 0) {
1118 // Should be unreachable
1119 jniThrowException(env, "java/lang/IllegalArgumentException",
1120 "Exposure time too long");
1121 return;
1122 }
1123 }
1124
1125 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
1126 BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001127 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001128
1129 }
1130
1131 {
1132 // ISO speed ratings
1133 camera_metadata_entry entry =
1134 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001135 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001136
1137 int32_t tempIso = *(entry.data.i32);
1138 if (tempIso < 0) {
1139 jniThrowException(env, "java/lang/IllegalArgumentException",
1140 "Negative ISO value");
1141 return;
1142 }
1143
1144 if (tempIso > UINT16_MAX) {
1145 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1146 tempIso = UINT16_MAX;
1147 }
1148
1149 uint16_t iso = static_cast<uint16_t>(tempIso);
1150 BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001151 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001152 }
1153
1154 {
1155 // focal length
1156 camera_metadata_entry entry =
1157 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001158 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001159
1160 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1161 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001162 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001163 }
1164
1165 {
1166 // f number
1167 camera_metadata_entry entry =
1168 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001169 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001170
1171 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1172 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001173 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001174 }
1175
1176 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001177 // Set DNG version information
1178 uint8_t version[4] = {1, 4, 0, 0};
1179 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001180 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001181
1182 uint8_t backwardVersion[4] = {1, 1, 0, 0};
1183 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001184 env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001185 }
1186
1187 {
1188 // Set whitelevel
1189 camera_metadata_entry entry =
1190 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001191 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001192 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
1193 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001194 TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001195 }
1196
1197 {
1198 // Set default scale
1199 uint32_t defaultScale[4] = {1, 1, 1, 1};
1200 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001201 env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001202 }
1203
1204 bool singleIlluminant = false;
1205 {
1206 // Set calibration illuminants
1207 camera_metadata_entry entry1 =
1208 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001209 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001210 camera_metadata_entry entry2 =
1211 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1212 if (entry2.count == 0) {
1213 singleIlluminant = true;
1214 }
1215 uint16_t ref1 = entry1.data.u8[0];
1216
1217 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001218 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001219
1220 if (!singleIlluminant) {
1221 uint16_t ref2 = entry2.data.u8[0];
1222 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001223 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001224 }
1225 }
1226
1227 {
1228 // Set color transforms
1229 camera_metadata_entry entry1 =
1230 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001231 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001232
1233 int32_t colorTransform1[entry1.count * 2];
1234
1235 size_t ctr = 0;
1236 for(size_t i = 0; i < entry1.count; ++i) {
1237 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1238 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1239 }
1240
Ruben Brunk47e91f22014-05-28 18:38:42 -07001241 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1242 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001243
1244 if (!singleIlluminant) {
1245 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001246 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001247 int32_t colorTransform2[entry2.count * 2];
1248
1249 ctr = 0;
1250 for(size_t i = 0; i < entry2.count; ++i) {
1251 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1252 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1253 }
1254
Ruben Brunk47e91f22014-05-28 18:38:42 -07001255 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1256 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001257 }
1258 }
1259
1260 {
1261 // Set calibration transforms
1262 camera_metadata_entry entry1 =
1263 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001264 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001265
1266 int32_t calibrationTransform1[entry1.count * 2];
1267
1268 size_t ctr = 0;
1269 for(size_t i = 0; i < entry1.count; ++i) {
1270 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1271 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1272 }
1273
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001274 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count,
1275 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001276
1277 if (!singleIlluminant) {
1278 camera_metadata_entry entry2 =
1279 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001280 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001281 int32_t calibrationTransform2[entry2.count * 2];
1282
1283 ctr = 0;
1284 for(size_t i = 0; i < entry2.count; ++i) {
1285 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1286 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1287 }
1288
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001289 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count,
Andreas Gampe2377cd32014-11-11 00:23:02 -08001290 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001291 }
1292 }
1293
1294 {
1295 // Set forward transforms
1296 camera_metadata_entry entry1 =
1297 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001298 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001299
1300 int32_t forwardTransform1[entry1.count * 2];
1301
1302 size_t ctr = 0;
1303 for(size_t i = 0; i < entry1.count; ++i) {
1304 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1305 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1306 }
1307
1308 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001309 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001310
1311 if (!singleIlluminant) {
1312 camera_metadata_entry entry2 =
1313 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001314 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001315 int32_t forwardTransform2[entry2.count * 2];
1316
1317 ctr = 0;
1318 for(size_t i = 0; i < entry2.count; ++i) {
1319 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1320 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1321 }
1322
1323 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001324 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001325 }
1326 }
1327
1328 {
1329 // Set camera neutral
1330 camera_metadata_entry entry =
1331 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001332 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001333 uint32_t cameraNeutral[entry.count * 2];
1334
1335 size_t ctr = 0;
1336 for(size_t i = 0; i < entry.count; ++i) {
1337 cameraNeutral[ctr++] =
1338 static_cast<uint32_t>(entry.data.r[i].numerator);
1339 cameraNeutral[ctr++] =
1340 static_cast<uint32_t>(entry.data.r[i].denominator);
1341 }
1342
1343 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001344 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001345 }
1346
1347 {
1348 // Setup data strips
1349 // TODO: Switch to tiled implementation.
Ruben Brunk47e91f22014-05-28 18:38:42 -07001350 if (writer->addStrip(TIFF_IFD_0) != OK) {
1351 ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
1352 jniThrowException(env, "java/lang/IllegalStateException",
1353 "Failed to setup strip tags.");
1354 return;
1355 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001356 }
1357
1358 {
1359 // Setup default crop + crop origin tags
1360 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
1361 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
1362 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
1363 uint32_t defaultCropOrigin[] = {margin, margin};
Ruben Brunk0e679e72015-05-12 15:09:10 -07001364 uint32_t defaultCropSize[] = {imageWidth - 2 * margin, imageHeight - 2 * margin};
Ruben Brunkf967a542014-04-28 16:31:11 -07001365 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001366 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001367 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001368 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001369 }
1370 }
1371
1372 {
1373 // Setup unique camera model tag
1374 char model[PROPERTY_VALUE_MAX];
1375 property_get("ro.product.model", model, "");
1376
1377 char manufacturer[PROPERTY_VALUE_MAX];
1378 property_get("ro.product.manufacturer", manufacturer, "");
1379
1380 char brand[PROPERTY_VALUE_MAX];
1381 property_get("ro.product.brand", brand, "");
1382
1383 String8 cameraModel(model);
1384 cameraModel += "-";
1385 cameraModel += manufacturer;
1386 cameraModel += "-";
1387 cameraModel += brand;
1388
1389 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1390 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001391 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001392 }
1393
1394 {
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001395 // Setup sensor noise model
1396 camera_metadata_entry entry =
1397 results.find(ANDROID_SENSOR_NOISE_PROFILE);
1398
Ruben Brunkd70132c2014-08-22 16:24:49 -07001399 const status_t numPlaneColors = 3;
1400 const status_t numCfaChannels = 4;
1401
1402 uint8_t cfaOut[numCfaChannels];
1403 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
1404 jniThrowException(env, "java/lang/IllegalArgumentException",
1405 "Invalid CFA from camera characteristics");
1406 return;
1407 }
1408
1409 double noiseProfile[numPlaneColors * 2];
1410
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001411 if (entry.count > 0) {
Ruben Brunkd70132c2014-08-22 16:24:49 -07001412 if (entry.count != numCfaChannels * 2) {
Dan Albert46d84442014-11-18 16:07:51 -08001413 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1414 "in characteristics, no noise profile tag written...",
1415 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001416 } else {
1417 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1418 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1419
1420 BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
1421 noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
1422 } else {
1423 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1424 " tag written...", __FUNCTION__);
1425 }
1426 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001427 } else {
1428 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1429 __FUNCTION__);
1430 }
1431 }
1432
1433 {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001434 // Set up opcode List 2
1435 OpcodeListBuilder builder;
1436 status_t err = OK;
1437
1438 // Set up lens shading map
Ruben Brunkf967a542014-04-28 16:31:11 -07001439 camera_metadata_entry entry1 =
1440 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001441
1442 uint32_t lsmWidth = 0;
1443 uint32_t lsmHeight = 0;
1444
1445 if (entry1.count != 0) {
1446 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1447 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1448 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001449
1450 camera_metadata_entry entry2 =
1451 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001452
1453 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001454 err = builder.addGainMapsForMetadata(lsmWidth,
1455 lsmHeight,
1456 0,
1457 0,
1458 imageHeight,
1459 imageWidth,
1460 opcodeCfaLayout,
1461 entry2.data.f);
1462 if (err != OK) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001463 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1464 jniThrowRuntimeException(env, "failed to add lens shading map.");
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001465 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001466 }
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001467 }
1468
Ruben Brunkfe816622015-06-16 23:03:09 -07001469 size_t listSize = builder.getSize();
1470 uint8_t opcodeListBuf[listSize];
1471 err = builder.buildOpList(opcodeListBuf);
1472 if (err == OK) {
1473 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
1474 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
1475 } else {
1476 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1477 "map.", __FUNCTION__);
1478 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1479 " correction and lens shading map");
1480 return;
1481 }
1482 }
1483
1484 {
1485 // Set up opcode List 3
1486 OpcodeListBuilder builder;
1487 status_t err = OK;
1488
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001489 // Set up rectilinear distortion correction
1490 camera_metadata_entry entry3 =
1491 results.find(ANDROID_LENS_RADIAL_DISTORTION);
1492 camera_metadata_entry entry4 =
1493 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION);
1494
1495 if (entry3.count == 6 && entry4.count == 5) {
1496 float cx = entry4.data.f[/*c_x*/2];
1497 float cy = entry4.data.f[/*c_y*/3];
1498 err = builder.addWarpRectilinearForMetadata(entry3.data.f, imageWidth, imageHeight, cx,
1499 cy);
1500 if (err != OK) {
1501 ALOGE("%s: Could not add distortion correction.", __FUNCTION__);
1502 jniThrowRuntimeException(env, "failed to add distortion correction.");
1503 return;
1504 }
1505 }
1506
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001507 size_t listSize = builder.getSize();
1508 uint8_t opcodeListBuf[listSize];
1509 err = builder.buildOpList(opcodeListBuf);
1510 if (err == OK) {
Ruben Brunkfe816622015-06-16 23:03:09 -07001511 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf,
1512 TIFF_IFD_0), env, TAG_OPCODELIST3, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001513 } else {
Ruben Brunk5f2368d2015-06-05 17:03:49 -07001514 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading"
1515 "map.", __FUNCTION__);
1516 jniThrowRuntimeException(env, "failed to construct opcode list for distortion"
1517 " correction and lens shading map");
1518 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001519 }
1520 }
1521
Ruben Brunk47e91f22014-05-28 18:38:42 -07001522 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001523}
1524
1525static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1526 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001527 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001528}
1529
Ruben Brunk47e91f22014-05-28 18:38:42 -07001530static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001531 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001532
1533 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1534 if (writer == NULL) {
1535 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1536 jniThrowException(env, "java/lang/AssertionError",
1537 "setOrientation called with uninitialized DngCreator");
1538 return;
1539 }
1540
1541 uint16_t orientation = static_cast<uint16_t>(orient);
1542 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1543 TAG_ORIENTATION, writer);
1544
1545 // Set main image orientation also if in a separate IFD
1546 if (writer->hasIfd(TIFF_IFD_SUB1)) {
1547 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1548 TAG_ORIENTATION, writer);
1549 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001550}
1551
Ruben Brunk47e91f22014-05-28 18:38:42 -07001552static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001553 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001554
1555 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1556 if (writer == NULL) {
1557 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1558 jniThrowException(env, "java/lang/AssertionError",
1559 "setDescription called with uninitialized DngCreator");
1560 return;
1561 }
1562
1563 const char* desc = env->GetStringUTFChars(description, NULL);
1564 size_t len = strlen(desc) + 1;
1565
1566 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1567 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1568 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1569 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1570 }
1571
1572 env->ReleaseStringUTFChars(description, desc);
Ruben Brunkf967a542014-04-28 16:31:11 -07001573}
1574
Ruben Brunk47e91f22014-05-28 18:38:42 -07001575static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1576 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001577 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001578
1579 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1580 if (writer == NULL) {
1581 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1582 jniThrowException(env, "java/lang/AssertionError",
1583 "setGpsTags called with uninitialized DngCreator");
1584 return;
1585 }
1586
1587 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1588 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1589 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1590 TIFF_IFD_0);
1591 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1592 return;
1593 }
1594 }
1595
1596 const jsize GPS_VALUE_LENGTH = 6;
1597 jsize latLen = env->GetArrayLength(latTag);
1598 jsize longLen = env->GetArrayLength(longTag);
1599 jsize timeLen = env->GetArrayLength(timeTag);
1600 if (latLen != GPS_VALUE_LENGTH) {
1601 jniThrowException(env, "java/lang/IllegalArgumentException",
1602 "invalid latitude tag length");
1603 return;
1604 } else if (longLen != GPS_VALUE_LENGTH) {
1605 jniThrowException(env, "java/lang/IllegalArgumentException",
1606 "invalid longitude tag length");
1607 return;
1608 } else if (timeLen != GPS_VALUE_LENGTH) {
1609 jniThrowException(env, "java/lang/IllegalArgumentException",
1610 "invalid time tag length");
1611 return;
1612 }
1613
1614 uint32_t latitude[GPS_VALUE_LENGTH];
1615 uint32_t longitude[GPS_VALUE_LENGTH];
1616 uint32_t timestamp[GPS_VALUE_LENGTH];
1617
1618 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1619 reinterpret_cast<jint*>(&latitude));
1620 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1621 reinterpret_cast<jint*>(&longitude));
1622 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1623 reinterpret_cast<jint*>(&timestamp));
1624
1625 const jsize GPS_REF_LENGTH = 2;
1626 const jsize GPS_DATE_LENGTH = 11;
1627 uint8_t latitudeRef[GPS_REF_LENGTH];
1628 uint8_t longitudeRef[GPS_REF_LENGTH];
1629 uint8_t date[GPS_DATE_LENGTH];
1630
1631 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1632 latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1633 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1634 longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1635
1636 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1637 date[GPS_DATE_LENGTH - 1] = '\0';
1638
1639 {
1640 uint8_t version[] = {2, 3, 0, 0};
1641 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1642 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1643 }
1644
1645 {
1646 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1647 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1648 }
1649
1650 {
1651 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1652 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1653 }
1654
1655 {
1656 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1657 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1658 }
1659
1660 {
1661 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1662 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1663 }
1664
1665 {
1666 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1667 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1668 }
1669
1670 {
1671 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1672 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1673 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001674}
1675
Ruben Brunk47e91f22014-05-28 18:38:42 -07001676static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1677 jint height) {
1678 ALOGV("%s:", __FUNCTION__);
1679
1680 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1681 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1682 if (writer == NULL || context == NULL) {
1683 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1684 jniThrowException(env, "java/lang/AssertionError",
1685 "setThumbnail called with uninitialized DngCreator");
1686 return;
1687 }
1688
1689 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1690 jlong capacity = env->GetDirectBufferCapacity(buffer);
Andreas Gampe0f0b4912014-11-12 08:03:48 -08001691 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001692 jniThrowExceptionFmt(env, "java/lang/AssertionError",
1693 "Invalid size %d for thumbnail, expected size was %d",
1694 capacity, fullSize);
1695 return;
1696 }
1697
1698 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1699 if (pixelBytes == NULL) {
1700 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1701 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1702 return;
1703 }
1704
1705 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1706 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1707 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1708 TIFF_IFD_0);
1709 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1710 return;
1711 }
1712
1713 Vector<uint16_t> tagsToMove;
1714 tagsToMove.add(TAG_ORIENTATION);
1715 tagsToMove.add(TAG_NEWSUBFILETYPE);
1716 tagsToMove.add(TAG_BITSPERSAMPLE);
1717 tagsToMove.add(TAG_COMPRESSION);
1718 tagsToMove.add(TAG_IMAGEWIDTH);
1719 tagsToMove.add(TAG_IMAGELENGTH);
1720 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1721 tagsToMove.add(TAG_BLACKLEVEL);
1722 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1723 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1724 tagsToMove.add(TAG_PLANARCONFIGURATION);
1725 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1726 tagsToMove.add(TAG_CFAPATTERN);
1727 tagsToMove.add(TAG_CFAPLANECOLOR);
1728 tagsToMove.add(TAG_CFALAYOUT);
1729 tagsToMove.add(TAG_XRESOLUTION);
1730 tagsToMove.add(TAG_YRESOLUTION);
1731 tagsToMove.add(TAG_RESOLUTIONUNIT);
1732 tagsToMove.add(TAG_WHITELEVEL);
1733 tagsToMove.add(TAG_DEFAULTSCALE);
1734 tagsToMove.add(TAG_ROWSPERSTRIP);
1735 tagsToMove.add(TAG_STRIPBYTECOUNTS);
1736 tagsToMove.add(TAG_STRIPOFFSETS);
1737 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1738 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1739 tagsToMove.add(TAG_OPCODELIST2);
Ruben Brunkfe816622015-06-16 23:03:09 -07001740 tagsToMove.add(TAG_OPCODELIST3);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001741
1742 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1743 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1744 return;
1745 }
1746
1747 // Make sure both IFDs get the same orientation tag
1748 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1749 if (orientEntry != NULL) {
1750 writer->addEntry(orientEntry, TIFF_IFD_0);
1751 }
1752 }
1753
1754 // Setup thumbnail tags
1755
1756 {
1757 // Set photometric interpretation
1758 uint16_t interpretation = 2; // RGB
1759 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1760 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1761 }
1762
1763 {
1764 // Set planar configuration
1765 uint16_t config = 1; // Chunky
1766 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1767 env, TAG_PLANARCONFIGURATION, writer);
1768 }
1769
1770 {
1771 // Set samples per pixel
1772 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1773 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1774 env, TAG_SAMPLESPERPIXEL, writer);
1775 }
1776
1777 {
1778 // Set bits per sample
1779 uint16_t bits = BITS_PER_RGB_SAMPLE;
1780 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1781 TAG_BITSPERSAMPLE, writer);
1782 }
1783
1784 {
1785 // Set subfiletype
1786 uint32_t subfileType = 1; // Thumbnail image
1787 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1788 TAG_NEWSUBFILETYPE, writer);
1789 }
1790
1791 {
1792 // Set compression
1793 uint16_t compression = 1; // None
1794 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1795 TAG_COMPRESSION, writer);
1796 }
1797
1798 {
1799 // Set dimensions
1800 uint32_t uWidth = static_cast<uint32_t>(width);
1801 uint32_t uHeight = static_cast<uint32_t>(height);
1802 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1803 TAG_IMAGEWIDTH, writer);
1804 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1805 TAG_IMAGELENGTH, writer);
1806 }
1807
1808 {
1809 // x resolution
1810 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1811 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1812 env, TAG_XRESOLUTION, writer);
1813
1814 // y resolution
1815 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1816 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1817 env, TAG_YRESOLUTION, writer);
1818
1819 uint16_t unit = 2; // inches
1820 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1821 env, TAG_RESOLUTIONUNIT, writer);
1822 }
1823
1824 {
1825 // Setup data strips
1826 if (writer->addStrip(TIFF_IFD_0) != OK) {
1827 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1828 jniThrowException(env, "java/lang/IllegalStateException",
1829 "Failed to setup thumbnail strip tags.");
1830 return;
1831 }
1832 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1833 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1834 jniThrowException(env, "java/lang/IllegalStateException",
1835 "Failed to setup main image strip tags.");
1836 return;
1837 }
1838 }
1839
1840 if (!context->setThumbnail(pixelBytes, width, height)) {
1841 jniThrowException(env, "java/lang/IllegalStateException",
1842 "Failed to set thumbnail.");
1843 return;
1844 }
1845}
1846
1847// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07001848static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001849 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1850 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001851 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08001852 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
1853 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1854 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001855 uint32_t rStride = static_cast<uint32_t>(rowStride);
1856 uint32_t pStride = static_cast<uint32_t>(pixStride);
1857 uint32_t uWidth = static_cast<uint32_t>(width);
1858 uint32_t uHeight = static_cast<uint32_t>(height);
1859 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07001860
1861 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1862 if(env->ExceptionCheck()) {
1863 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1864 return;
1865 }
1866
Ruben Brunkf967a542014-04-28 16:31:11 -07001867 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001868 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1869 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001870 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1871 jniThrowException(env, "java/lang/AssertionError",
1872 "Write called with uninitialized DngCreator");
1873 return;
1874 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001875
1876 // Validate DNG header
1877 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001878 return;
1879 }
1880
Ruben Brunk47e91f22014-05-28 18:38:42 -07001881 sp<JniInputByteBuffer> inBuf;
1882 Vector<StripSource*> sources;
1883 sp<DirectStripSource> thumbnailSource;
1884 uint32_t targetIfd = TIFF_IFD_0;
1885
1886 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1887
1888 if (hasThumbnail) {
1889 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1890 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1891 uint32_t thumbWidth = context->getThumbnailWidth();
1892 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1893 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1894 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1895 SAMPLES_PER_RGB_PIXEL);
1896 sources.add(thumbnailSource.get());
1897 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001898 }
1899
Ruben Brunk47e91f22014-05-28 18:38:42 -07001900 if (isDirect) {
1901 size_t fullSize = rStride * uHeight;
1902 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1903 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1904 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1905 "Invalid size %d for Image, size given in metadata is %d at current stride",
1906 capacity, fullSize);
1907 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001908 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001909
Ruben Brunk47e91f22014-05-28 18:38:42 -07001910 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1911 if (pixelBytes == NULL) {
1912 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1913 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1914 return;
1915 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001916
Ruben Brunk47e91f22014-05-28 18:38:42 -07001917 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1918 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1919 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1920 sources.add(&stripSource);
1921
1922 status_t ret = OK;
1923 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1924 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001925 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001926 jniThrowExceptionFmt(env, "java/io/IOException",
1927 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001928 }
1929 return;
1930 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001931 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001932 inBuf = new JniInputByteBuffer(env, inBuffer);
1933
1934 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1935 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1936 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1937 sources.add(&stripSource);
1938
1939 status_t ret = OK;
1940 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1941 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1942 if (!env->ExceptionCheck()) {
1943 jniThrowExceptionFmt(env, "java/io/IOException",
1944 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001945 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001946 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001947 }
1948 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001949}
1950
1951static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001952 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001953 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001954
1955 uint32_t rowStride = width * BYTES_PER_SAMPLE;
1956 uint32_t pixStride = BYTES_PER_SAMPLE;
1957 uint32_t uWidth = static_cast<uint32_t>(width);
1958 uint32_t uHeight = static_cast<uint32_t>(height);
1959 uint64_t uOffset = static_cast<uint32_t>(offset);
1960
Dan Albert46d84442014-11-18 16:07:51 -08001961 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
1962 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1963 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001964
1965 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08001966 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001967 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1968 return;
1969 }
1970
1971 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1972 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1973 if (writer == NULL || context == NULL) {
1974 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1975 jniThrowException(env, "java/lang/AssertionError",
1976 "Write called with uninitialized DngCreator");
1977 return;
1978 }
1979
1980 // Validate DNG header
1981 if (!validateDngHeader(env, writer, width, height)) {
1982 return;
1983 }
1984
1985 sp<DirectStripSource> thumbnailSource;
1986 uint32_t targetIfd = TIFF_IFD_0;
1987 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1988 Vector<StripSource*> sources;
1989
1990 if (hasThumbnail) {
1991 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1992 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1993 uint32_t width = context->getThumbnailWidth();
1994 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1995 width, context->getThumbnailHeight(), bytesPerPixel,
1996 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1997 SAMPLES_PER_RGB_PIXEL);
1998 sources.add(thumbnailSource.get());
1999 targetIfd = TIFF_IFD_SUB1;
2000 }
2001
2002 sp<JniInputStream> in = new JniInputStream(env, inStream);
2003
2004 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
2005 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
2006 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
2007 sources.add(&stripSource);
2008
2009 status_t ret = OK;
2010 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
2011 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
2012 if (!env->ExceptionCheck()) {
2013 jniThrowExceptionFmt(env, "java/io/IOException",
2014 "Encountered error %d while writing file.", ret);
2015 }
2016 return;
2017 }
Ruben Brunkf967a542014-04-28 16:31:11 -07002018}
2019
2020} /*extern "C" */
2021
2022static JNINativeMethod gDngCreatorMethods[] = {
2023 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
2024 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07002025 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
2026 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07002027 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
2028 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002029 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
2030 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
2031 (void*) DngCreator_nativeSetGpsTags},
2032 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
2033 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002034 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07002035 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07002036 (void*) DngCreator_nativeWriteInputStream},
2037};
2038
Ruben Brunkb6079002014-05-22 12:33:54 -07002039int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08002040 return RegisterMethodsOrDie(env,
2041 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07002042}