blob: 4d3aff6a675526e96e63e10d2092b3af8e3a1046 [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 Brunka3fdec82015-01-09 13:48:31 -0800374 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700375 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.
633 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
634 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
635
636 if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
637 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
638 "Metadata width %d doesn't match image width %d", metadataWidth, width);
639 return false;
640 }
641
642 if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
643 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
644 "Metadata height %d doesn't match image height %d", metadataHeight, height);
645 return false;
646 }
647
648 return true;
649}
650
651static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
652 const Vector<uint16_t>& entries) {
653 for (size_t i = 0; i < entries.size(); ++i) {
654 uint16_t tagId = entries[i];
655 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
656 if (entry == NULL) {
657 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
658 ifdFrom);
659 return BAD_VALUE;
660 }
661 if (writer->addEntry(entry, ifdTo) != OK) {
662 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
663 ifdFrom);
664 return BAD_VALUE;
665 }
666 writer->removeEntry(tagId, ifdFrom);
667 }
668 return OK;
669}
670
Ruben Brunkd70132c2014-08-22 16:24:49 -0700671/**
672 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
673 * Returns OK on success, or a negative error code if the CFA enum was invalid.
674 */
675static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
676 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
677 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
678 cfaEnum);
679 switch(cfa) {
680 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
681 cfaOut[0] = 0;
682 cfaOut[1] = 1;
683 cfaOut[2] = 1;
684 cfaOut[3] = 2;
685 break;
686 }
687 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
688 cfaOut[0] = 1;
689 cfaOut[1] = 0;
690 cfaOut[2] = 2;
691 cfaOut[3] = 1;
692 break;
693 }
694 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
695 cfaOut[0] = 1;
696 cfaOut[1] = 2;
697 cfaOut[2] = 0;
698 cfaOut[3] = 1;
699 break;
700 }
701 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
702 cfaOut[0] = 2;
703 cfaOut[1] = 1;
704 cfaOut[2] = 1;
705 cfaOut[3] = 0;
706 break;
707 }
708 default: {
709 return BAD_VALUE;
710 }
711 }
712 return OK;
713}
714
715/**
716 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
717 * RGGB for an unknown enum.
718 */
719static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
720 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
721 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
722 cfaEnum);
723 switch(cfa) {
724 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
725 return OpcodeListBuilder::CFA_RGGB;
726 }
727 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
728 return OpcodeListBuilder::CFA_GRBG;
729 }
730 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
731 return OpcodeListBuilder::CFA_GBRG;
732 }
733 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
734 return OpcodeListBuilder::CFA_BGGR;
735 }
736 default: {
737 return OpcodeListBuilder::CFA_RGGB;
738 }
739 }
740}
741
742/**
743 * For each color plane, find the corresponding noise profile coefficients given in the
744 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color
745 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
746 *
747 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
748 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
749 * coefficients.
750 * numChannels - the number of noise profile coefficient pairs and color channels given in
751 * the perChannelNoiseProfile and cfa arguments, respectively.
752 * planeColors - the color planes in the noise profile output.
753 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
754 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
755 *
756 * returns OK, or a negative error code on failure.
757 */
758static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
759 size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
760 /*out*/double* noiseProfile) {
761
762 for (size_t p = 0; p < numPlanes; ++p) {
763 size_t S = p * 2;
764 size_t O = p * 2 + 1;
765
766 noiseProfile[S] = 0;
767 noiseProfile[O] = 0;
768 bool uninitialized = true;
769 for (size_t c = 0; c < numChannels; ++c) {
770 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
771 noiseProfile[S] = perChannelNoiseProfile[c * 2];
772 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
773 uninitialized = false;
774 }
775 }
776 if (uninitialized) {
Dan Albert46d84442014-11-18 16:07:51 -0800777 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu",
778 __FUNCTION__, p);
Ruben Brunkd70132c2014-08-22 16:24:49 -0700779 return BAD_VALUE;
780 }
781 }
782 return OK;
783}
784
Ruben Brunk47e91f22014-05-28 18:38:42 -0700785// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700786extern "C" {
787
Ruben Brunk47e91f22014-05-28 18:38:42 -0700788static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700789 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700790 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700791 gDngCreatorClassInfo.mNativeContext));
792}
793
Ruben Brunk47e91f22014-05-28 18:38:42 -0700794static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700795 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700796 NativeContext* current = DngCreator_getNativeContext(env, thiz);
797
798 if (context != NULL) {
799 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700800 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700801
Ruben Brunkf967a542014-04-28 16:31:11 -0700802 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700803 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700804 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700805
Ruben Brunkf967a542014-04-28 16:31:11 -0700806 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700807 reinterpret_cast<jlong>(context.get()));
808}
809
810static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
811 ALOGV("%s:", __FUNCTION__);
812 NativeContext* current = DngCreator_getNativeContext(env, thiz);
813 if (current) {
814 return current->getWriter();
815 }
816 return NULL;
Ruben Brunkf967a542014-04-28 16:31:11 -0700817}
818
819static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
820 ALOGV("%s:", __FUNCTION__);
821
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800822 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env,
823 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700824
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800825 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream");
826 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env,
827 outputStreamClazz, "write", "([BII)V");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700828
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800829 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream");
830 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I");
831 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700832
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800833 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer");
834 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env,
835 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;");
Ruben Brunkf967a542014-04-28 16:31:11 -0700836}
837
838static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700839 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700840 ALOGV("%s:", __FUNCTION__);
841 CameraMetadata characteristics;
842 CameraMetadata results;
843 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
844 jniThrowException(env, "java/lang/AssertionError",
845 "No native metadata defined for camera characteristics.");
846 return;
847 }
848 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
849 jniThrowException(env, "java/lang/AssertionError",
850 "No native metadata defined for capture results.");
851 return;
852 }
853
Ruben Brunk47e91f22014-05-28 18:38:42 -0700854 sp<NativeContext> nativeContext = new NativeContext();
855 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700856
857 writer->addIfd(TIFF_IFD_0);
858
859 status_t err = OK;
860
861 const uint32_t samplesPerPixel = 1;
862 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
Ruben Brunkf967a542014-04-28 16:31:11 -0700863 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 Gampe2377cd32014-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) {
Dan Albert46d84442014-11-18 16:07:51 -08001410 ALOGW("%s: Invalid entry count %zu for noise profile returned "
1411 "in characteristics, no noise profile tag written...",
1412 __FUNCTION__, entry.count);
Ruben Brunkd70132c2014-08-22 16:24:49 -07001413 } else {
1414 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
1415 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {
1416
1417 BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
1418 noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
1419 } else {
1420 ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
1421 " tag written...", __FUNCTION__);
1422 }
1423 }
Ruben Brunkb1971dc2014-07-23 13:23:00 -07001424 } else {
1425 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.",
1426 __FUNCTION__);
1427 }
1428 }
1429
1430 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001431 // Setup opcode List 2
1432 camera_metadata_entry entry1 =
1433 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001434
1435 uint32_t lsmWidth = 0;
1436 uint32_t lsmHeight = 0;
1437
1438 if (entry1.count != 0) {
1439 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1440 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1441 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001442
1443 camera_metadata_entry entry2 =
1444 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001445
1446 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001447
1448 OpcodeListBuilder builder;
1449 status_t err = builder.addGainMapsForMetadata(lsmWidth,
1450 lsmHeight,
1451 0,
1452 0,
1453 imageHeight,
1454 imageWidth,
1455 opcodeCfaLayout,
1456 entry2.data.f);
1457 if (err == OK) {
1458 size_t listSize = builder.getSize();
1459 uint8_t opcodeListBuf[listSize];
1460 err = builder.buildOpList(opcodeListBuf);
1461 if (err == OK) {
1462 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001463 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001464 } else {
1465 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
1466 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
1467 }
1468 } else {
1469 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1470 jniThrowRuntimeException(env, "failed to add lens shading map.");
1471 }
1472 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001473 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1474 __FUNCTION__);
Ruben Brunkf967a542014-04-28 16:31:11 -07001475 }
1476 }
1477
Ruben Brunk47e91f22014-05-28 18:38:42 -07001478 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001479}
1480
1481static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1482 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001483 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001484}
1485
Ruben Brunk47e91f22014-05-28 18:38:42 -07001486static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001487 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001488
1489 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1490 if (writer == NULL) {
1491 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1492 jniThrowException(env, "java/lang/AssertionError",
1493 "setOrientation called with uninitialized DngCreator");
1494 return;
1495 }
1496
1497 uint16_t orientation = static_cast<uint16_t>(orient);
1498 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1499 TAG_ORIENTATION, writer);
1500
1501 // Set main image orientation also if in a separate IFD
1502 if (writer->hasIfd(TIFF_IFD_SUB1)) {
1503 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1504 TAG_ORIENTATION, writer);
1505 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001506}
1507
Ruben Brunk47e91f22014-05-28 18:38:42 -07001508static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001509 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001510
1511 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1512 if (writer == NULL) {
1513 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1514 jniThrowException(env, "java/lang/AssertionError",
1515 "setDescription called with uninitialized DngCreator");
1516 return;
1517 }
1518
1519 const char* desc = env->GetStringUTFChars(description, NULL);
1520 size_t len = strlen(desc) + 1;
1521
1522 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1523 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1524 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1525 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1526 }
1527
1528 env->ReleaseStringUTFChars(description, desc);
Ruben Brunkf967a542014-04-28 16:31:11 -07001529}
1530
Ruben Brunk47e91f22014-05-28 18:38:42 -07001531static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1532 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001533 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001534
1535 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1536 if (writer == NULL) {
1537 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1538 jniThrowException(env, "java/lang/AssertionError",
1539 "setGpsTags called with uninitialized DngCreator");
1540 return;
1541 }
1542
1543 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1544 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1545 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1546 TIFF_IFD_0);
1547 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1548 return;
1549 }
1550 }
1551
1552 const jsize GPS_VALUE_LENGTH = 6;
1553 jsize latLen = env->GetArrayLength(latTag);
1554 jsize longLen = env->GetArrayLength(longTag);
1555 jsize timeLen = env->GetArrayLength(timeTag);
1556 if (latLen != GPS_VALUE_LENGTH) {
1557 jniThrowException(env, "java/lang/IllegalArgumentException",
1558 "invalid latitude tag length");
1559 return;
1560 } else if (longLen != GPS_VALUE_LENGTH) {
1561 jniThrowException(env, "java/lang/IllegalArgumentException",
1562 "invalid longitude tag length");
1563 return;
1564 } else if (timeLen != GPS_VALUE_LENGTH) {
1565 jniThrowException(env, "java/lang/IllegalArgumentException",
1566 "invalid time tag length");
1567 return;
1568 }
1569
1570 uint32_t latitude[GPS_VALUE_LENGTH];
1571 uint32_t longitude[GPS_VALUE_LENGTH];
1572 uint32_t timestamp[GPS_VALUE_LENGTH];
1573
1574 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1575 reinterpret_cast<jint*>(&latitude));
1576 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1577 reinterpret_cast<jint*>(&longitude));
1578 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1579 reinterpret_cast<jint*>(&timestamp));
1580
1581 const jsize GPS_REF_LENGTH = 2;
1582 const jsize GPS_DATE_LENGTH = 11;
1583 uint8_t latitudeRef[GPS_REF_LENGTH];
1584 uint8_t longitudeRef[GPS_REF_LENGTH];
1585 uint8_t date[GPS_DATE_LENGTH];
1586
1587 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1588 latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1589 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1590 longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1591
1592 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1593 date[GPS_DATE_LENGTH - 1] = '\0';
1594
1595 {
1596 uint8_t version[] = {2, 3, 0, 0};
1597 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1598 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1599 }
1600
1601 {
1602 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1603 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1604 }
1605
1606 {
1607 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1608 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1609 }
1610
1611 {
1612 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1613 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1614 }
1615
1616 {
1617 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1618 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1619 }
1620
1621 {
1622 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1623 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1624 }
1625
1626 {
1627 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1628 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1629 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001630}
1631
Ruben Brunk47e91f22014-05-28 18:38:42 -07001632static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1633 jint height) {
1634 ALOGV("%s:", __FUNCTION__);
1635
1636 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1637 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1638 if (writer == NULL || context == NULL) {
1639 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1640 jniThrowException(env, "java/lang/AssertionError",
1641 "setThumbnail called with uninitialized DngCreator");
1642 return;
1643 }
1644
1645 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1646 jlong capacity = env->GetDirectBufferCapacity(buffer);
Andreas Gampe0f0b4912014-11-12 08:03:48 -08001647 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001648 jniThrowExceptionFmt(env, "java/lang/AssertionError",
1649 "Invalid size %d for thumbnail, expected size was %d",
1650 capacity, fullSize);
1651 return;
1652 }
1653
1654 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1655 if (pixelBytes == NULL) {
1656 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1657 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1658 return;
1659 }
1660
1661 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1662 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1663 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1664 TIFF_IFD_0);
1665 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1666 return;
1667 }
1668
1669 Vector<uint16_t> tagsToMove;
1670 tagsToMove.add(TAG_ORIENTATION);
1671 tagsToMove.add(TAG_NEWSUBFILETYPE);
1672 tagsToMove.add(TAG_BITSPERSAMPLE);
1673 tagsToMove.add(TAG_COMPRESSION);
1674 tagsToMove.add(TAG_IMAGEWIDTH);
1675 tagsToMove.add(TAG_IMAGELENGTH);
1676 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1677 tagsToMove.add(TAG_BLACKLEVEL);
1678 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1679 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1680 tagsToMove.add(TAG_PLANARCONFIGURATION);
1681 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1682 tagsToMove.add(TAG_CFAPATTERN);
1683 tagsToMove.add(TAG_CFAPLANECOLOR);
1684 tagsToMove.add(TAG_CFALAYOUT);
1685 tagsToMove.add(TAG_XRESOLUTION);
1686 tagsToMove.add(TAG_YRESOLUTION);
1687 tagsToMove.add(TAG_RESOLUTIONUNIT);
1688 tagsToMove.add(TAG_WHITELEVEL);
1689 tagsToMove.add(TAG_DEFAULTSCALE);
1690 tagsToMove.add(TAG_ROWSPERSTRIP);
1691 tagsToMove.add(TAG_STRIPBYTECOUNTS);
1692 tagsToMove.add(TAG_STRIPOFFSETS);
1693 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1694 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1695 tagsToMove.add(TAG_OPCODELIST2);
1696
1697 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1698 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1699 return;
1700 }
1701
1702 // Make sure both IFDs get the same orientation tag
1703 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1704 if (orientEntry != NULL) {
1705 writer->addEntry(orientEntry, TIFF_IFD_0);
1706 }
1707 }
1708
1709 // Setup thumbnail tags
1710
1711 {
1712 // Set photometric interpretation
1713 uint16_t interpretation = 2; // RGB
1714 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1715 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1716 }
1717
1718 {
1719 // Set planar configuration
1720 uint16_t config = 1; // Chunky
1721 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1722 env, TAG_PLANARCONFIGURATION, writer);
1723 }
1724
1725 {
1726 // Set samples per pixel
1727 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1728 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1729 env, TAG_SAMPLESPERPIXEL, writer);
1730 }
1731
1732 {
1733 // Set bits per sample
1734 uint16_t bits = BITS_PER_RGB_SAMPLE;
1735 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1736 TAG_BITSPERSAMPLE, writer);
1737 }
1738
1739 {
1740 // Set subfiletype
1741 uint32_t subfileType = 1; // Thumbnail image
1742 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1743 TAG_NEWSUBFILETYPE, writer);
1744 }
1745
1746 {
1747 // Set compression
1748 uint16_t compression = 1; // None
1749 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1750 TAG_COMPRESSION, writer);
1751 }
1752
1753 {
1754 // Set dimensions
1755 uint32_t uWidth = static_cast<uint32_t>(width);
1756 uint32_t uHeight = static_cast<uint32_t>(height);
1757 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1758 TAG_IMAGEWIDTH, writer);
1759 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1760 TAG_IMAGELENGTH, writer);
1761 }
1762
1763 {
1764 // x resolution
1765 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1766 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1767 env, TAG_XRESOLUTION, writer);
1768
1769 // y resolution
1770 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1771 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1772 env, TAG_YRESOLUTION, writer);
1773
1774 uint16_t unit = 2; // inches
1775 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1776 env, TAG_RESOLUTIONUNIT, writer);
1777 }
1778
1779 {
1780 // Setup data strips
1781 if (writer->addStrip(TIFF_IFD_0) != OK) {
1782 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1783 jniThrowException(env, "java/lang/IllegalStateException",
1784 "Failed to setup thumbnail strip tags.");
1785 return;
1786 }
1787 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1788 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1789 jniThrowException(env, "java/lang/IllegalStateException",
1790 "Failed to setup main image strip tags.");
1791 return;
1792 }
1793 }
1794
1795 if (!context->setThumbnail(pixelBytes, width, height)) {
1796 jniThrowException(env, "java/lang/IllegalStateException",
1797 "Failed to set thumbnail.");
1798 return;
1799 }
1800}
1801
1802// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07001803static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001804 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1805 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001806 ALOGV("%s:", __FUNCTION__);
Dan Albert46d84442014-11-18 16:07:51 -08001807 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, "
1808 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1809 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001810 uint32_t rStride = static_cast<uint32_t>(rowStride);
1811 uint32_t pStride = static_cast<uint32_t>(pixStride);
1812 uint32_t uWidth = static_cast<uint32_t>(width);
1813 uint32_t uHeight = static_cast<uint32_t>(height);
1814 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07001815
1816 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1817 if(env->ExceptionCheck()) {
1818 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1819 return;
1820 }
1821
Ruben Brunkf967a542014-04-28 16:31:11 -07001822 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001823 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1824 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001825 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1826 jniThrowException(env, "java/lang/AssertionError",
1827 "Write called with uninitialized DngCreator");
1828 return;
1829 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001830
1831 // Validate DNG header
1832 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001833 return;
1834 }
1835
Ruben Brunk47e91f22014-05-28 18:38:42 -07001836 sp<JniInputByteBuffer> inBuf;
1837 Vector<StripSource*> sources;
1838 sp<DirectStripSource> thumbnailSource;
1839 uint32_t targetIfd = TIFF_IFD_0;
1840
1841 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1842
1843 if (hasThumbnail) {
1844 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1845 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1846 uint32_t thumbWidth = context->getThumbnailWidth();
1847 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1848 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1849 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1850 SAMPLES_PER_RGB_PIXEL);
1851 sources.add(thumbnailSource.get());
1852 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001853 }
1854
Ruben Brunk47e91f22014-05-28 18:38:42 -07001855 if (isDirect) {
1856 size_t fullSize = rStride * uHeight;
1857 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1858 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1859 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1860 "Invalid size %d for Image, size given in metadata is %d at current stride",
1861 capacity, fullSize);
1862 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001863 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001864
Ruben Brunk47e91f22014-05-28 18:38:42 -07001865 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1866 if (pixelBytes == NULL) {
1867 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1868 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1869 return;
1870 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001871
Ruben Brunk47e91f22014-05-28 18:38:42 -07001872 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1873 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1874 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1875 sources.add(&stripSource);
1876
1877 status_t ret = OK;
1878 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1879 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001880 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001881 jniThrowExceptionFmt(env, "java/io/IOException",
1882 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001883 }
1884 return;
1885 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001886 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001887 inBuf = new JniInputByteBuffer(env, inBuffer);
1888
1889 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1890 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1891 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1892 sources.add(&stripSource);
1893
1894 status_t ret = OK;
1895 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1896 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1897 if (!env->ExceptionCheck()) {
1898 jniThrowExceptionFmt(env, "java/io/IOException",
1899 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001900 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001901 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001902 }
1903 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001904}
1905
1906static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001907 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001908 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001909
1910 uint32_t rowStride = width * BYTES_PER_SAMPLE;
1911 uint32_t pixStride = BYTES_PER_SAMPLE;
1912 uint32_t uWidth = static_cast<uint32_t>(width);
1913 uint32_t uHeight = static_cast<uint32_t>(height);
1914 uint64_t uOffset = static_cast<uint32_t>(offset);
1915
Dan Albert46d84442014-11-18 16:07:51 -08001916 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, "
1917 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width,
1918 height, rowStride, pixStride, offset);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001919
1920 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
Dan Albert46d84442014-11-18 16:07:51 -08001921 if (env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001922 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1923 return;
1924 }
1925
1926 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1927 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1928 if (writer == NULL || context == NULL) {
1929 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1930 jniThrowException(env, "java/lang/AssertionError",
1931 "Write called with uninitialized DngCreator");
1932 return;
1933 }
1934
1935 // Validate DNG header
1936 if (!validateDngHeader(env, writer, width, height)) {
1937 return;
1938 }
1939
1940 sp<DirectStripSource> thumbnailSource;
1941 uint32_t targetIfd = TIFF_IFD_0;
1942 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1943 Vector<StripSource*> sources;
1944
1945 if (hasThumbnail) {
1946 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1947 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1948 uint32_t width = context->getThumbnailWidth();
1949 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1950 width, context->getThumbnailHeight(), bytesPerPixel,
1951 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1952 SAMPLES_PER_RGB_PIXEL);
1953 sources.add(thumbnailSource.get());
1954 targetIfd = TIFF_IFD_SUB1;
1955 }
1956
1957 sp<JniInputStream> in = new JniInputStream(env, inStream);
1958
1959 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1960 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
1961 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1962 sources.add(&stripSource);
1963
1964 status_t ret = OK;
1965 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1966 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1967 if (!env->ExceptionCheck()) {
1968 jniThrowExceptionFmt(env, "java/io/IOException",
1969 "Encountered error %d while writing file.", ret);
1970 }
1971 return;
1972 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001973}
1974
1975} /*extern "C" */
1976
Daniel Micay76f6a862015-09-19 17:31:01 -04001977static const JNINativeMethod gDngCreatorMethods[] = {
Ruben Brunkf967a542014-04-28 16:31:11 -07001978 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
1979 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001980 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1981 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07001982 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
1983 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001984 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
1985 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
1986 (void*) DngCreator_nativeSetGpsTags},
1987 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
1988 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001989 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001990 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001991 (void*) DngCreator_nativeWriteInputStream},
1992};
1993
Ruben Brunkb6079002014-05-22 12:33:54 -07001994int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Andreas Gampeed6b9df2014-11-20 22:02:20 -08001995 return RegisterMethodsOrDie(env,
1996 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07001997}