blob: 3a3328f75230fca058c191837c78ed99a8780ced [file] [log] [blame]
Ruben Brunkf967a542014-04-28 16:31:11 -07001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "DngCreator_JNI"
19
20#include <system/camera_metadata.h>
21#include <camera/CameraMetadata.h>
22#include <img_utils/DngUtils.h>
23#include <img_utils/TagDefinitions.h>
24#include <img_utils/TiffIfd.h>
25#include <img_utils/TiffWriter.h>
26#include <img_utils/Output.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070027#include <img_utils/Input.h>
28#include <img_utils/StripSource.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070029
30#include <utils/Log.h>
31#include <utils/Errors.h>
32#include <utils/StrongPointer.h>
33#include <utils/RefBase.h>
Ruben Brunk47e91f22014-05-28 18:38:42 -070034#include <utils/Vector.h>
Ruben Brunkf967a542014-04-28 16:31:11 -070035#include <cutils/properties.h>
36
Ruben Brunkb8df8e02014-06-02 22:59:45 -070037#include <string.h>
38
Ruben Brunkf967a542014-04-28 16:31:11 -070039#include "android_runtime/AndroidRuntime.h"
40#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
41
42#include <jni.h>
43#include <JNIHelp.h>
44
45using namespace android;
46using namespace img_utils;
47
Ruben Brunk47e91f22014-05-28 18:38:42 -070048#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070049 if ((expr) != OK) { \
50 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070051 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070052 return; \
53 }
54
Ruben Brunk47e91f22014-05-28 18:38:42 -070055#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \
Ruben Brunkf967a542014-04-28 16:31:11 -070056 if (entry.count == 0) { \
57 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
Ruben Brunk47e91f22014-05-28 18:38:42 -070058 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
Ruben Brunkf967a542014-04-28 16:31:11 -070059 return; \
60 }
61
Ruben Brunkb6079002014-05-22 12:33:54 -070062#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
Ruben Brunkf967a542014-04-28 16:31:11 -070063
64static struct {
65 jfieldID mNativeContext;
66} gDngCreatorClassInfo;
67
68static struct {
69 jmethodID mWriteMethod;
70} gOutputStreamClassInfo;
71
Ruben Brunk47e91f22014-05-28 18:38:42 -070072static struct {
73 jmethodID mReadMethod;
74 jmethodID mSkipMethod;
75} gInputStreamClassInfo;
76
77static struct {
78 jmethodID mGetMethod;
79} gInputByteBufferClassInfo;
80
Ruben Brunkf967a542014-04-28 16:31:11 -070081enum {
82 BITS_PER_SAMPLE = 16,
83 BYTES_PER_SAMPLE = 2,
Ruben Brunk47e91f22014-05-28 18:38:42 -070084 BYTES_PER_RGB_PIXEL = 3,
85 BITS_PER_RGB_SAMPLE = 8,
86 BYTES_PER_RGB_SAMPLE = 1,
87 SAMPLES_PER_RGB_PIXEL = 3,
88 SAMPLES_PER_RAW_PIXEL = 1,
89 TIFF_IFD_0 = 0,
90 TIFF_IFD_SUB1 = 1,
91 TIFF_IFD_GPSINFO = 2,
Ruben Brunkf967a542014-04-28 16:31:11 -070092};
93
94// ----------------------------------------------------------------------------
95
Ruben Brunk47e91f22014-05-28 18:38:42 -070096/**
97 * Container class for the persistent native context.
98 */
99
100class NativeContext : public LightRefBase<NativeContext> {
101
102public:
103 NativeContext();
104 virtual ~NativeContext();
105
106 TiffWriter* getWriter();
107
108 uint32_t getThumbnailWidth();
109 uint32_t getThumbnailHeight();
110 const uint8_t* getThumbnail();
111
112 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height);
113
114private:
115 Vector<uint8_t> mCurrentThumbnail;
116 TiffWriter mWriter;
117 uint32_t mThumbnailWidth;
118 uint32_t mThumbnailHeight;
119};
120
121NativeContext::NativeContext() : mThumbnailWidth(0), mThumbnailHeight(0) {}
122
123NativeContext::~NativeContext() {}
124
125TiffWriter* NativeContext::getWriter() {
126 return &mWriter;
127}
128
129uint32_t NativeContext::getThumbnailWidth() {
130 return mThumbnailWidth;
131}
132
133uint32_t NativeContext::getThumbnailHeight() {
134 return mThumbnailHeight;
135}
136
137const uint8_t* NativeContext::getThumbnail() {
138 return mCurrentThumbnail.array();
139}
140
141bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) {
142 mThumbnailWidth = width;
143 mThumbnailHeight = height;
144
145 size_t size = BYTES_PER_RGB_PIXEL * width * height;
146 if (mCurrentThumbnail.resize(size) < 0) {
147 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__);
148 return false;
149 }
150
151 uint8_t* thumb = mCurrentThumbnail.editArray();
152 memcpy(thumb, buffer, size);
153 return true;
154}
155
156// End of NativeContext
157// ----------------------------------------------------------------------------
158
159/**
160 * Wrapper class for a Java OutputStream.
161 *
162 * This class is not intended to be used across JNI calls.
163 */
Ruben Brunkf967a542014-04-28 16:31:11 -0700164class JniOutputStream : public Output, public LightRefBase<JniOutputStream> {
165public:
166 JniOutputStream(JNIEnv* env, jobject outStream);
167
168 virtual ~JniOutputStream();
169
170 status_t open();
Ruben Brunk47e91f22014-05-28 18:38:42 -0700171
Ruben Brunkf967a542014-04-28 16:31:11 -0700172 status_t write(const uint8_t* buf, size_t offset, size_t count);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700173
Ruben Brunkf967a542014-04-28 16:31:11 -0700174 status_t close();
175private:
176 enum {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700177 BYTE_ARRAY_LENGTH = 4096
Ruben Brunkf967a542014-04-28 16:31:11 -0700178 };
179 jobject mOutputStream;
180 JNIEnv* mEnv;
181 jbyteArray mByteArray;
182};
183
184JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream),
185 mEnv(env) {
186 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
187 if (mByteArray == NULL) {
188 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
189 }
190}
191
192JniOutputStream::~JniOutputStream() {
193 mEnv->DeleteLocalRef(mByteArray);
194}
195
196status_t JniOutputStream::open() {
197 // Do nothing
198 return OK;
199}
200
201status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) {
202 while(count > 0) {
203 size_t len = BYTE_ARRAY_LENGTH;
204 len = (count > len) ? len : count;
205 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset));
206
207 if (mEnv->ExceptionCheck()) {
208 return BAD_VALUE;
209 }
210
211 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray,
212 0, len);
213
214 if (mEnv->ExceptionCheck()) {
215 return BAD_VALUE;
216 }
217
218 count -= len;
219 offset += len;
220 }
221 return OK;
222}
223
224status_t JniOutputStream::close() {
225 // Do nothing
226 return OK;
227}
228
Ruben Brunk47e91f22014-05-28 18:38:42 -0700229// End of JniOutputStream
Ruben Brunkf967a542014-04-28 16:31:11 -0700230// ----------------------------------------------------------------------------
231
Ruben Brunk47e91f22014-05-28 18:38:42 -0700232/**
233 * Wrapper class for a Java InputStream.
234 *
235 * This class is not intended to be used across JNI calls.
236 */
237class JniInputStream : public Input, public LightRefBase<JniInputStream> {
238public:
239 JniInputStream(JNIEnv* env, jobject inStream);
240
241 status_t open();
242
243 status_t close();
244
245 ssize_t read(uint8_t* buf, size_t offset, size_t count);
246
247 ssize_t skip(size_t count);
248
249 virtual ~JniInputStream();
250private:
251 enum {
252 BYTE_ARRAY_LENGTH = 4096
253 };
254 jobject mInStream;
255 JNIEnv* mEnv;
256 jbyteArray mByteArray;
257
258};
259
260JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) {
261 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
262 if (mByteArray == NULL) {
263 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
264 }
265}
266
267JniInputStream::~JniInputStream() {
268 mEnv->DeleteLocalRef(mByteArray);
269}
270
271ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) {
272
273 jint realCount = BYTE_ARRAY_LENGTH;
274 if (count < BYTE_ARRAY_LENGTH) {
275 realCount = count;
276 }
277 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0,
278 realCount);
279
280 if (actual < 0) {
281 return NOT_ENOUGH_DATA;
282 }
283
284 if (mEnv->ExceptionCheck()) {
285 return BAD_VALUE;
286 }
287
288 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset));
289 if (mEnv->ExceptionCheck()) {
290 return BAD_VALUE;
291 }
292 return actual;
293}
294
295ssize_t JniInputStream::skip(size_t count) {
296 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod,
297 static_cast<jlong>(count));
298
299 if (mEnv->ExceptionCheck()) {
300 return BAD_VALUE;
301 }
302 if (actual < 0) {
303 return NOT_ENOUGH_DATA;
304 }
305 return actual;
306}
307
308status_t JniInputStream::open() {
309 // Do nothing
310 return OK;
311}
312
313status_t JniInputStream::close() {
314 // Do nothing
315 return OK;
316}
317
318// End of JniInputStream
319// ----------------------------------------------------------------------------
320
321/**
322 * Wrapper class for a non-direct Java ByteBuffer.
323 *
324 * This class is not intended to be used across JNI calls.
325 */
326class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> {
327public:
328 JniInputByteBuffer(JNIEnv* env, jobject inBuf);
329
330 status_t open();
331
332 status_t close();
333
334 ssize_t read(uint8_t* buf, size_t offset, size_t count);
335
336 virtual ~JniInputByteBuffer();
337private:
338 enum {
339 BYTE_ARRAY_LENGTH = 4096
340 };
341 jobject mInBuf;
342 JNIEnv* mEnv;
343 jbyteArray mByteArray;
344};
345
346JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) {
347 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH);
348 if (mByteArray == NULL) {
349 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array.");
350 }
351}
352
353JniInputByteBuffer::~JniInputByteBuffer() {
354 mEnv->DeleteLocalRef(mByteArray);
355}
356
357ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) {
358 jint realCount = BYTE_ARRAY_LENGTH;
359 if (count < BYTE_ARRAY_LENGTH) {
360 realCount = count;
361 }
362
363 mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, mByteArray, 0,
364 realCount);
365
366 if (mEnv->ExceptionCheck()) {
367 return BAD_VALUE;
368 }
369
370 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset));
371 if (mEnv->ExceptionCheck()) {
372 return BAD_VALUE;
373 }
374 return realCount;
375}
376
377status_t JniInputByteBuffer::open() {
378 // Do nothing
379 return OK;
380}
381
382status_t JniInputByteBuffer::close() {
383 // Do nothing
384 return OK;
385}
386
387// End of JniInputByteBuffer
388// ----------------------------------------------------------------------------
389
390/**
391 * StripSource subclass for Input types.
392 *
393 * This class is not intended to be used across JNI calls.
394 */
395
396class InputStripSource : public StripSource, public LightRefBase<InputStripSource> {
397public:
398 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height,
399 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample,
400 uint32_t samplesPerPixel);
401
402 virtual ~InputStripSource();
403
404 virtual status_t writeToStream(Output& stream, uint32_t count);
405
406 virtual uint32_t getIfd() const;
407protected:
408 uint32_t mIfd;
409 Input* mInput;
410 uint32_t mWidth;
411 uint32_t mHeight;
412 uint32_t mPixStride;
413 uint32_t mRowStride;
414 uint64_t mOffset;
415 JNIEnv* mEnv;
416 uint32_t mBytesPerSample;
417 uint32_t mSamplesPerPixel;
418};
419
420InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width,
421 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
422 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input),
423 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride),
424 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
425 mSamplesPerPixel(samplesPerPixel) {}
426
427InputStripSource::~InputStripSource() {}
428
429status_t InputStripSource::writeToStream(Output& stream, uint32_t count) {
430 status_t err = OK;
431 uint32_t fullSize = mRowStride * mHeight;
432 jlong offset = mOffset;
433
434 if (fullSize != count) {
435 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
436 fullSize);
437 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
438 return BAD_VALUE;
439 }
440
441 // Skip offset
442 while (offset > 0) {
443 ssize_t skipped = mInput->skip(offset);
444 if (skipped <= 0) {
445 if (skipped == NOT_ENOUGH_DATA || skipped == 0) {
446 jniThrowExceptionFmt(mEnv, "java/io/IOException",
447 "Early EOF encountered in skip, not enough pixel data for image of size %u",
448 fullSize);
449 skipped = NOT_ENOUGH_DATA;
450 } else {
451 if (!mEnv->ExceptionCheck()) {
452 jniThrowException(mEnv, "java/io/IOException",
453 "Error encountered while skip bytes in input stream.");
454 }
455 }
456
457 return skipped;
458 }
459 offset -= skipped;
460 }
461
462 Vector<uint8_t> row;
463 if (row.resize(mRowStride) < 0) {
464 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector.");
465 return BAD_VALUE;
466 }
467
468 uint8_t* rowBytes = row.editArray();
469
470 for (uint32_t i = 0; i < mHeight; ++i) {
471 size_t rowFillAmt = 0;
472 size_t rowSize = mPixStride;
473
474 while (rowFillAmt < mRowStride) {
475 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize);
476 if (bytesRead <= 0) {
477 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) {
478 jniThrowExceptionFmt(mEnv, "java/io/IOException",
479 "Early EOF encountered, not enough pixel data for image of size %u",
480 fullSize);
481 bytesRead = NOT_ENOUGH_DATA;
482 } else {
483 if (!mEnv->ExceptionCheck()) {
484 jniThrowException(mEnv, "java/io/IOException",
485 "Error encountered while reading");
486 }
487 }
488 return bytesRead;
489 }
490 rowFillAmt += bytesRead;
491 rowSize -= bytesRead;
492 }
493
494 if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
495 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__);
496
497 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK ||
498 mEnv->ExceptionCheck()) {
499 if (!mEnv->ExceptionCheck()) {
500 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
501 }
502 return BAD_VALUE;
503 }
504 } else {
505 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__);
506 jniThrowException(mEnv, "java/lang/IllegalStateException",
507 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
508 return BAD_VALUE;
509
510 // TODO: Add support for non-contiguous pixels if needed.
511 }
512 }
513 return OK;
514}
515
516uint32_t InputStripSource::getIfd() const {
517 return mIfd;
518}
519
520// End of InputStripSource
521// ----------------------------------------------------------------------------
522
523/**
524 * StripSource subclass for direct buffer types.
525 *
526 * This class is not intended to be used across JNI calls.
527 */
528
529class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> {
530public:
531 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width,
532 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset,
533 uint32_t bytesPerSample, uint32_t samplesPerPixel);
534
535 virtual ~DirectStripSource();
536
537 virtual status_t writeToStream(Output& stream, uint32_t count);
538
539 virtual uint32_t getIfd() const;
540protected:
541 uint32_t mIfd;
542 const uint8_t* mPixelBytes;
543 uint32_t mWidth;
544 uint32_t mHeight;
545 uint32_t mPixStride;
546 uint32_t mRowStride;
547 uint16_t mOffset;
548 JNIEnv* mEnv;
549 uint32_t mBytesPerSample;
550 uint32_t mSamplesPerPixel;
551};
552
553DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd,
554 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride,
555 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd),
556 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride),
557 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample),
558 mSamplesPerPixel(samplesPerPixel) {}
559
560DirectStripSource::~DirectStripSource() {}
561
562status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) {
563 uint32_t fullSize = mRowStride * mHeight;
564
565 if (fullSize != count) {
566 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count,
567 fullSize);
568 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write");
569 return BAD_VALUE;
570 }
571
572 if (mPixStride == mBytesPerSample * mSamplesPerPixel
573 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) {
574 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__);
575
576 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) {
577 if (!mEnv->ExceptionCheck()) {
578 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
579 }
580 return BAD_VALUE;
581 }
582 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) {
583 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__);
584
585 for (size_t i = 0; i < mHeight; ++i) {
586 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK ||
587 mEnv->ExceptionCheck()) {
588 if (!mEnv->ExceptionCheck()) {
589 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data");
590 }
591 return BAD_VALUE;
592 }
593 }
594 } else {
595 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__);
596
597 jniThrowException(mEnv, "java/lang/IllegalStateException",
598 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous");
599 return BAD_VALUE;
600
601 // TODO: Add support for non-contiguous pixels if needed.
602 }
603 return OK;
604
605}
606
607uint32_t DirectStripSource::getIfd() const {
608 return mIfd;
609}
610
611// End of DirectStripSource
612// ----------------------------------------------------------------------------
613
614static bool validateDngHeader(JNIEnv* env, TiffWriter* writer, jint width, jint height) {
615 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
616
617 // TODO: handle lens shading map, etc. conversions for other raw buffer sizes.
618 uint32_t metadataWidth = *(writer->getEntry(TAG_IMAGEWIDTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
619 uint32_t metadataHeight = *(writer->getEntry(TAG_IMAGELENGTH, (hasThumbnail) ? TIFF_IFD_SUB1 : TIFF_IFD_0)->getData<uint32_t>());
620
621 if (width < 0 || metadataWidth != static_cast<uint32_t>(width)) {
622 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
623 "Metadata width %d doesn't match image width %d", metadataWidth, width);
624 return false;
625 }
626
627 if (height < 0 || metadataHeight != static_cast<uint32_t>(height)) {
628 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \
629 "Metadata height %d doesn't match image height %d", metadataHeight, height);
630 return false;
631 }
632
633 return true;
634}
635
636static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo,
637 const Vector<uint16_t>& entries) {
638 for (size_t i = 0; i < entries.size(); ++i) {
639 uint16_t tagId = entries[i];
640 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
641 if (entry == NULL) {
642 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
643 ifdFrom);
644 return BAD_VALUE;
645 }
646 if (writer->addEntry(entry, ifdTo) != OK) {
647 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
648 ifdFrom);
649 return BAD_VALUE;
650 }
651 writer->removeEntry(tagId, ifdFrom);
652 }
653 return OK;
654}
655
656// ----------------------------------------------------------------------------
Ruben Brunkf967a542014-04-28 16:31:11 -0700657extern "C" {
658
Ruben Brunk47e91f22014-05-28 18:38:42 -0700659static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700660 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700661 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz,
Ruben Brunkf967a542014-04-28 16:31:11 -0700662 gDngCreatorClassInfo.mNativeContext));
663}
664
Ruben Brunk47e91f22014-05-28 18:38:42 -0700665static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700666 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700667 NativeContext* current = DngCreator_getNativeContext(env, thiz);
668
669 if (context != NULL) {
670 context->incStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700671 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700672
Ruben Brunkf967a542014-04-28 16:31:11 -0700673 if (current) {
Ruben Brunk47e91f22014-05-28 18:38:42 -0700674 current->decStrong((void*) DngCreator_setNativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -0700675 }
Ruben Brunk47e91f22014-05-28 18:38:42 -0700676
Ruben Brunkf967a542014-04-28 16:31:11 -0700677 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700678 reinterpret_cast<jlong>(context.get()));
679}
680
681static TiffWriter* DngCreator_getCreator(JNIEnv* env, jobject thiz) {
682 ALOGV("%s:", __FUNCTION__);
683 NativeContext* current = DngCreator_getNativeContext(env, thiz);
684 if (current) {
685 return current->getWriter();
686 }
687 return NULL;
Ruben Brunkf967a542014-04-28 16:31:11 -0700688}
689
690static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) {
691 ALOGV("%s:", __FUNCTION__);
692
693 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz,
Ruben Brunkb6079002014-05-22 12:33:54 -0700694 ANDROID_DNGCREATOR_CTX_JNI_ID, "J");
Ruben Brunkf967a542014-04-28 16:31:11 -0700695 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL,
Ruben Brunkb6079002014-05-22 12:33:54 -0700696 "can't find android/hardware/camera2/DngCreator.%s",
697 ANDROID_DNGCREATOR_CTX_JNI_ID);
Ruben Brunkf967a542014-04-28 16:31:11 -0700698
699 jclass outputStreamClazz = env->FindClass("java/io/OutputStream");
700 LOG_ALWAYS_FATAL_IF(outputStreamClazz == NULL, "Can't find java/io/OutputStream class");
701 gOutputStreamClassInfo.mWriteMethod = env->GetMethodID(outputStreamClazz, "write", "([BII)V");
702 LOG_ALWAYS_FATAL_IF(gOutputStreamClassInfo.mWriteMethod == NULL, "Can't find write method");
Ruben Brunk47e91f22014-05-28 18:38:42 -0700703
704 jclass inputStreamClazz = env->FindClass("java/io/InputStream");
705 LOG_ALWAYS_FATAL_IF(inputStreamClazz == NULL, "Can't find java/io/InputStream class");
706 gInputStreamClassInfo.mReadMethod = env->GetMethodID(inputStreamClazz, "read", "([BII)I");
707 LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mReadMethod == NULL, "Can't find read method");
708 gInputStreamClassInfo.mSkipMethod = env->GetMethodID(inputStreamClazz, "skip", "(J)J");
709 LOG_ALWAYS_FATAL_IF(gInputStreamClassInfo.mSkipMethod == NULL, "Can't find skip method");
710
711 jclass inputBufferClazz = env->FindClass("java/nio/ByteBuffer");
712 LOG_ALWAYS_FATAL_IF(inputBufferClazz == NULL, "Can't find java/nio/ByteBuffer class");
713 gInputByteBufferClassInfo.mGetMethod = env->GetMethodID(inputBufferClazz, "get",
714 "([BII)Ljava/nio/ByteBuffer;");
715 LOG_ALWAYS_FATAL_IF(gInputByteBufferClassInfo.mGetMethod == NULL, "Can't find get method");
Ruben Brunkf967a542014-04-28 16:31:11 -0700716}
717
718static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700719 jobject resultsPtr, jstring formattedCaptureTime) {
Ruben Brunkf967a542014-04-28 16:31:11 -0700720 ALOGV("%s:", __FUNCTION__);
721 CameraMetadata characteristics;
722 CameraMetadata results;
723 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) {
724 jniThrowException(env, "java/lang/AssertionError",
725 "No native metadata defined for camera characteristics.");
726 return;
727 }
728 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) {
729 jniThrowException(env, "java/lang/AssertionError",
730 "No native metadata defined for capture results.");
731 return;
732 }
733
Ruben Brunk47e91f22014-05-28 18:38:42 -0700734 sp<NativeContext> nativeContext = new NativeContext();
735 TiffWriter* writer = nativeContext->getWriter();
Ruben Brunkf967a542014-04-28 16:31:11 -0700736
737 writer->addIfd(TIFF_IFD_0);
738
739 status_t err = OK;
740
741 const uint32_t samplesPerPixel = 1;
742 const uint32_t bitsPerSample = BITS_PER_SAMPLE;
743 const uint32_t bitsPerByte = BITS_PER_SAMPLE / BYTES_PER_SAMPLE;
744 uint32_t imageWidth = 0;
745 uint32_t imageHeight = 0;
746
747 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
748
749 // TODO: Greensplit.
Ruben Brunkf967a542014-04-28 16:31:11 -0700750 // TODO: Add remaining non-essential tags
Ruben Brunk47e91f22014-05-28 18:38:42 -0700751
752 // Setup main image tags
753
Ruben Brunkf967a542014-04-28 16:31:11 -0700754 {
755 // Set orientation
756 uint16_t orientation = 1; // Normal
757 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700758 TAG_ORIENTATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700759 }
760
761 {
762 // Set subfiletype
763 uint32_t subfileType = 0; // Main image
764 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700765 TAG_NEWSUBFILETYPE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700766 }
767
768 {
769 // Set bits per sample
770 uint16_t bits = static_cast<uint16_t>(bitsPerSample);
771 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700772 TAG_BITSPERSAMPLE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700773 }
774
775 {
776 // Set compression
777 uint16_t compression = 1; // None
778 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700779 TAG_COMPRESSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700780 }
781
782 {
783 // Set dimensions
784 camera_metadata_entry entry =
785 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700786 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700787 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]);
788 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]);
789 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &width, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700790 TAG_IMAGEWIDTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700791 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700792 TAG_IMAGELENGTH, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700793 imageWidth = width;
794 imageHeight = height;
795 }
796
797 {
798 // Set photometric interpretation
Ruben Brunk47e91f22014-05-28 18:38:42 -0700799 uint16_t interpretation = 32803; // CFA
Ruben Brunkf967a542014-04-28 16:31:11 -0700800 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700801 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700802 }
803
804 {
805 // Set blacklevel tags
806 camera_metadata_entry entry =
807 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700808 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700809 const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
810 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700811 TAG_BLACKLEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700812
813 uint16_t repeatDim[2] = {2, 2};
814 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700815 TAG_BLACKLEVELREPEATDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700816 }
817
818 {
819 // Set samples per pixel
820 uint16_t samples = static_cast<uint16_t>(samplesPerPixel);
821 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700822 env, TAG_SAMPLESPERPIXEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700823 }
824
825 {
826 // Set planar configuration
827 uint16_t config = 1; // Chunky
828 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700829 env, TAG_PLANARCONFIGURATION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700830 }
831
832 {
833 // Set CFA pattern dimensions
834 uint16_t repeatDim[2] = {2, 2};
835 BAIL_IF_INVALID(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700836 env, TAG_CFAREPEATPATTERNDIM, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700837 }
838
839 {
840 // Set CFA pattern
841 camera_metadata_entry entry =
842 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
Ruben Brunk47e91f22014-05-28 18:38:42 -0700843 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700844 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
845 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
846 entry.data.u8[0]);
847 switch(cfa) {
848 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
849 uint8_t cfa[4] = {0, 1, 1, 2};
850 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700851 env, TAG_CFAPATTERN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700852 opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
853 break;
854 }
855 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
856 uint8_t cfa[4] = {1, 0, 2, 1};
857 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700858 env, TAG_CFAPATTERN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700859 opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
860 break;
861 }
862 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
863 uint8_t cfa[4] = {1, 2, 0, 1};
864 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700865 env, TAG_CFAPATTERN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700866 opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
867 break;
868 }
869 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
870 uint8_t cfa[4] = {2, 1, 1, 0};
871 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700872 env, TAG_CFAPATTERN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700873 opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
874 break;
875 }
876 default: {
877 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
878 "Invalid metadata for tag %d", TAG_CFAPATTERN);
879 return;
880 }
881 }
882 }
883
884 {
885 // Set CFA plane color
886 uint8_t cfaPlaneColor[3] = {0, 1, 2};
887 BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700888 env, TAG_CFAPLANECOLOR, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700889 }
890
891 {
892 // Set CFA layout
893 uint16_t cfaLayout = 1;
894 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700895 env, TAG_CFALAYOUT, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -0700896 }
897
898 {
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700899 // image description
900 uint8_t imageDescription = '\0'; // empty
901 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700902 env, TAG_IMAGEDESCRIPTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700903 }
904
905 {
906 // make
907 char manufacturer[PROPERTY_VALUE_MAX];
908
909 // Use "" to represent unknown make as suggested in TIFF/EP spec.
910 property_get("ro.product.manufacturer", manufacturer, "");
911 uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
912
913 BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700914 TIFF_IFD_0), env, TAG_MAKE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700915 }
916
917 {
918 // model
919 char model[PROPERTY_VALUE_MAX];
920
921 // Use "" to represent unknown model as suggested in TIFF/EP spec.
922 property_get("ro.product.model", model, "");
923 uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
924
925 BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700926 TIFF_IFD_0), env, TAG_MODEL, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700927 }
928
929 {
930 // x resolution
931 uint32_t xres[] = { 72, 1 }; // default 72 ppi
932 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700933 env, TAG_XRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700934
935 // y resolution
936 uint32_t yres[] = { 72, 1 }; // default 72 ppi
937 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700938 env, TAG_YRESOLUTION, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700939
940 uint16_t unit = 2; // inches
941 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700942 env, TAG_RESOLUTIONUNIT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700943 }
944
945 {
946 // software
947 char software[PROPERTY_VALUE_MAX];
948 property_get("ro.build.fingerprint", software, "");
949 uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
950 BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
Ruben Brunk47e91f22014-05-28 18:38:42 -0700951 TIFF_IFD_0), env, TAG_SOFTWARE, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700952 }
953
954 {
955 // datetime
956 const size_t DATETIME_COUNT = 20;
957 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
958
959 size_t len = strlen(captureTime) + 1;
960 if (len != DATETIME_COUNT) {
961 jniThrowException(env, "java/lang/IllegalArgumentException",
962 "Timestamp string length is not required 20 characters");
963 return;
964 }
965
Ruben Brunk47e91f22014-05-28 18:38:42 -0700966 if (writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
967 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
968 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
969 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
970 "Invalid metadata for tag %x", TAG_DATETIME);
971 return;
972 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700973
974 // datetime original
Ruben Brunk47e91f22014-05-28 18:38:42 -0700975 if (writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
976 reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0) != OK) {
977 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
978 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
979 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
980 return;
981 }
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700982 env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
983 }
984
985 {
986 // TIFF/EP standard id
987 uint8_t standardId[] = { 1, 0, 0, 0 };
988 BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700989 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700990 }
991
992 {
993 // copyright
994 uint8_t copyright = '\0'; // empty
995 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, &copyright,
Ruben Brunk47e91f22014-05-28 18:38:42 -0700996 TIFF_IFD_0), env, TAG_COPYRIGHT, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -0700997 }
998
999 {
1000 // exposure time
1001 camera_metadata_entry entry =
1002 results.find(ANDROID_SENSOR_EXPOSURE_TIME);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001003 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001004
1005 int64_t exposureTime = *(entry.data.i64);
1006
1007 if (exposureTime < 0) {
1008 // Should be unreachable
1009 jniThrowException(env, "java/lang/IllegalArgumentException",
1010 "Negative exposure time in metadata");
1011 return;
1012 }
1013
1014 // Ensure exposure time doesn't overflow (for exposures > 4s)
1015 uint32_t denominator = 1000000000;
1016 while (exposureTime > UINT32_MAX) {
1017 exposureTime >>= 1;
1018 denominator >>= 1;
1019 if (denominator == 0) {
1020 // Should be unreachable
1021 jniThrowException(env, "java/lang/IllegalArgumentException",
1022 "Exposure time too long");
1023 return;
1024 }
1025 }
1026
1027 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
1028 BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001029 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001030
1031 }
1032
1033 {
1034 // ISO speed ratings
1035 camera_metadata_entry entry =
1036 results.find(ANDROID_SENSOR_SENSITIVITY);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001037 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001038
1039 int32_t tempIso = *(entry.data.i32);
1040 if (tempIso < 0) {
1041 jniThrowException(env, "java/lang/IllegalArgumentException",
1042 "Negative ISO value");
1043 return;
1044 }
1045
1046 if (tempIso > UINT16_MAX) {
1047 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
1048 tempIso = UINT16_MAX;
1049 }
1050
1051 uint16_t iso = static_cast<uint16_t>(tempIso);
1052 BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001053 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001054 }
1055
1056 {
1057 // focal length
1058 camera_metadata_entry entry =
1059 results.find(ANDROID_LENS_FOCAL_LENGTH);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001060 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001061
1062 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1063 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001064 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001065 }
1066
1067 {
1068 // f number
1069 camera_metadata_entry entry =
1070 results.find(ANDROID_LENS_APERTURE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001071 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001072
1073 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
1074 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001075 TIFF_IFD_0), env, TAG_FNUMBER, writer);
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001076 }
1077
1078 {
Ruben Brunkf967a542014-04-28 16:31:11 -07001079 // Set DNG version information
1080 uint8_t version[4] = {1, 4, 0, 0};
1081 BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001082 env, TAG_DNGVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001083
1084 uint8_t backwardVersion[4] = {1, 1, 0, 0};
1085 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001086 env, TAG_DNGBACKWARDVERSION, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001087 }
1088
1089 {
1090 // Set whitelevel
1091 camera_metadata_entry entry =
1092 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001093 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001094 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]);
1095 BAIL_IF_INVALID(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001096 TAG_WHITELEVEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001097 }
1098
1099 {
1100 // Set default scale
1101 uint32_t defaultScale[4] = {1, 1, 1, 1};
1102 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, TIFF_IFD_0),
Ruben Brunk47e91f22014-05-28 18:38:42 -07001103 env, TAG_DEFAULTSCALE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001104 }
1105
1106 bool singleIlluminant = false;
1107 {
1108 // Set calibration illuminants
1109 camera_metadata_entry entry1 =
1110 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001111 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001112 camera_metadata_entry entry2 =
1113 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2);
1114 if (entry2.count == 0) {
1115 singleIlluminant = true;
1116 }
1117 uint16_t ref1 = entry1.data.u8[0];
1118
1119 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001120 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001121
1122 if (!singleIlluminant) {
1123 uint16_t ref2 = entry2.data.u8[0];
1124 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001125 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001126 }
1127 }
1128
1129 {
1130 // Set color transforms
1131 camera_metadata_entry entry1 =
1132 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001133 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001134
1135 int32_t colorTransform1[entry1.count * 2];
1136
1137 size_t ctr = 0;
1138 for(size_t i = 0; i < entry1.count; ++i) {
1139 colorTransform1[ctr++] = entry1.data.r[i].numerator;
1140 colorTransform1[ctr++] = entry1.data.r[i].denominator;
1141 }
1142
Ruben Brunk47e91f22014-05-28 18:38:42 -07001143 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1,
1144 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001145
1146 if (!singleIlluminant) {
1147 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001148 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001149 int32_t colorTransform2[entry2.count * 2];
1150
1151 ctr = 0;
1152 for(size_t i = 0; i < entry2.count; ++i) {
1153 colorTransform2[ctr++] = entry2.data.r[i].numerator;
1154 colorTransform2[ctr++] = entry2.data.r[i].denominator;
1155 }
1156
Ruben Brunk47e91f22014-05-28 18:38:42 -07001157 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2,
1158 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001159 }
1160 }
1161
1162 {
1163 // Set calibration transforms
1164 camera_metadata_entry entry1 =
1165 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001166 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001167
1168 int32_t calibrationTransform1[entry1.count * 2];
1169
1170 size_t ctr = 0;
1171 for(size_t i = 0; i < entry1.count; ++i) {
1172 calibrationTransform1[ctr++] = entry1.data.r[i].numerator;
1173 calibrationTransform1[ctr++] = entry1.data.r[i].denominator;
1174 }
1175
1176 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, calibrationTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001177 TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001178
1179 if (!singleIlluminant) {
1180 camera_metadata_entry entry2 =
1181 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001182 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001183 int32_t calibrationTransform2[entry2.count * 2];
1184
1185 ctr = 0;
1186 for(size_t i = 0; i < entry2.count; ++i) {
1187 calibrationTransform2[ctr++] = entry2.data.r[i].numerator;
1188 calibrationTransform2[ctr++] = entry2.data.r[i].denominator;
1189 }
1190
1191 BAIL_IF_INVALID(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, calibrationTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001192 TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001193 }
1194 }
1195
1196 {
1197 // Set forward transforms
1198 camera_metadata_entry entry1 =
1199 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001200 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001201
1202 int32_t forwardTransform1[entry1.count * 2];
1203
1204 size_t ctr = 0;
1205 for(size_t i = 0; i < entry1.count; ++i) {
1206 forwardTransform1[ctr++] = entry1.data.r[i].numerator;
1207 forwardTransform1[ctr++] = entry1.data.r[i].denominator;
1208 }
1209
1210 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, forwardTransform1,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001211 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001212
1213 if (!singleIlluminant) {
1214 camera_metadata_entry entry2 =
1215 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001216 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001217 int32_t forwardTransform2[entry2.count * 2];
1218
1219 ctr = 0;
1220 for(size_t i = 0; i < entry2.count; ++i) {
1221 forwardTransform2[ctr++] = entry2.data.r[i].numerator;
1222 forwardTransform2[ctr++] = entry2.data.r[i].denominator;
1223 }
1224
1225 BAIL_IF_INVALID(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, forwardTransform2,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001226 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001227 }
1228 }
1229
1230 {
1231 // Set camera neutral
1232 camera_metadata_entry entry =
1233 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001234 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001235 uint32_t cameraNeutral[entry.count * 2];
1236
1237 size_t ctr = 0;
1238 for(size_t i = 0; i < entry.count; ++i) {
1239 cameraNeutral[ctr++] =
1240 static_cast<uint32_t>(entry.data.r[i].numerator);
1241 cameraNeutral[ctr++] =
1242 static_cast<uint32_t>(entry.data.r[i].denominator);
1243 }
1244
1245 BAIL_IF_INVALID(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001246 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001247 }
1248
1249 {
1250 // Setup data strips
1251 // TODO: Switch to tiled implementation.
Ruben Brunk47e91f22014-05-28 18:38:42 -07001252 if (writer->addStrip(TIFF_IFD_0) != OK) {
1253 ALOGE("%s: Could not setup strip tags.", __FUNCTION__);
1254 jniThrowException(env, "java/lang/IllegalStateException",
1255 "Failed to setup strip tags.");
1256 return;
1257 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001258 }
1259
1260 {
1261 // Setup default crop + crop origin tags
1262 uint32_t margin = 8; // Default margin recommended by Adobe for interpolation.
1263 uint32_t dimensionLimit = 128; // Smallest image dimension crop margin from.
1264 if (imageWidth >= dimensionLimit && imageHeight >= dimensionLimit) {
1265 uint32_t defaultCropOrigin[] = {margin, margin};
1266 uint32_t defaultCropSize[] = {imageWidth - margin, imageHeight - margin};
1267 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001268 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001269 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001270 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001271 }
1272 }
1273
1274 {
1275 // Setup unique camera model tag
1276 char model[PROPERTY_VALUE_MAX];
1277 property_get("ro.product.model", model, "");
1278
1279 char manufacturer[PROPERTY_VALUE_MAX];
1280 property_get("ro.product.manufacturer", manufacturer, "");
1281
1282 char brand[PROPERTY_VALUE_MAX];
1283 property_get("ro.product.brand", brand, "");
1284
1285 String8 cameraModel(model);
1286 cameraModel += "-";
1287 cameraModel += manufacturer;
1288 cameraModel += "-";
1289 cameraModel += brand;
1290
1291 BAIL_IF_INVALID(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
1292 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001293 TAG_UNIQUECAMERAMODEL, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001294 }
1295
1296 {
1297 // Setup opcode List 2
1298 camera_metadata_entry entry1 =
1299 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001300
1301 uint32_t lsmWidth = 0;
1302 uint32_t lsmHeight = 0;
1303
1304 if (entry1.count != 0) {
1305 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]);
1306 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
1307 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001308
1309 camera_metadata_entry entry2 =
1310 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001311
1312 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001313
1314 OpcodeListBuilder builder;
1315 status_t err = builder.addGainMapsForMetadata(lsmWidth,
1316 lsmHeight,
1317 0,
1318 0,
1319 imageHeight,
1320 imageWidth,
1321 opcodeCfaLayout,
1322 entry2.data.f);
1323 if (err == OK) {
1324 size_t listSize = builder.getSize();
1325 uint8_t opcodeListBuf[listSize];
1326 err = builder.buildOpList(opcodeListBuf);
1327 if (err == OK) {
1328 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001329 TIFF_IFD_0), env, TAG_OPCODELIST2, writer);
Ruben Brunkf967a542014-04-28 16:31:11 -07001330 } else {
1331 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__);
1332 jniThrowRuntimeException(env, "failed to construct lens shading map opcode.");
1333 }
1334 } else {
1335 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__);
1336 jniThrowRuntimeException(env, "failed to add lens shading map.");
1337 }
1338 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001339 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.",
1340 __FUNCTION__);
Ruben Brunkf967a542014-04-28 16:31:11 -07001341 }
1342 }
1343
Ruben Brunk47e91f22014-05-28 18:38:42 -07001344 DngCreator_setNativeContext(env, thiz, nativeContext);
Ruben Brunkf967a542014-04-28 16:31:11 -07001345}
1346
1347static void DngCreator_destroy(JNIEnv* env, jobject thiz) {
1348 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001349 DngCreator_setNativeContext(env, thiz, NULL);
Ruben Brunkf967a542014-04-28 16:31:11 -07001350}
1351
Ruben Brunk47e91f22014-05-28 18:38:42 -07001352static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001353 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001354
1355 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1356 if (writer == NULL) {
1357 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1358 jniThrowException(env, "java/lang/AssertionError",
1359 "setOrientation called with uninitialized DngCreator");
1360 return;
1361 }
1362
1363 uint16_t orientation = static_cast<uint16_t>(orient);
1364 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env,
1365 TAG_ORIENTATION, writer);
1366
1367 // Set main image orientation also if in a separate IFD
1368 if (writer->hasIfd(TIFF_IFD_SUB1)) {
1369 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env,
1370 TAG_ORIENTATION, writer);
1371 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001372}
1373
Ruben Brunk47e91f22014-05-28 18:38:42 -07001374static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001375 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001376
1377 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1378 if (writer == NULL) {
1379 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1380 jniThrowException(env, "java/lang/AssertionError",
1381 "setDescription called with uninitialized DngCreator");
1382 return;
1383 }
1384
1385 const char* desc = env->GetStringUTFChars(description, NULL);
1386 size_t len = strlen(desc) + 1;
1387
1388 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
1389 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) {
1390 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
1391 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
1392 }
1393
1394 env->ReleaseStringUTFChars(description, desc);
Ruben Brunkf967a542014-04-28 16:31:11 -07001395}
1396
Ruben Brunk47e91f22014-05-28 18:38:42 -07001397static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef,
1398 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001399 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001400
1401 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1402 if (writer == NULL) {
1403 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1404 jniThrowException(env, "java/lang/AssertionError",
1405 "setGpsTags called with uninitialized DngCreator");
1406 return;
1407 }
1408
1409 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) {
1410 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) {
1411 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO,
1412 TIFF_IFD_0);
1413 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO");
1414 return;
1415 }
1416 }
1417
1418 const jsize GPS_VALUE_LENGTH = 6;
1419 jsize latLen = env->GetArrayLength(latTag);
1420 jsize longLen = env->GetArrayLength(longTag);
1421 jsize timeLen = env->GetArrayLength(timeTag);
1422 if (latLen != GPS_VALUE_LENGTH) {
1423 jniThrowException(env, "java/lang/IllegalArgumentException",
1424 "invalid latitude tag length");
1425 return;
1426 } else if (longLen != GPS_VALUE_LENGTH) {
1427 jniThrowException(env, "java/lang/IllegalArgumentException",
1428 "invalid longitude tag length");
1429 return;
1430 } else if (timeLen != GPS_VALUE_LENGTH) {
1431 jniThrowException(env, "java/lang/IllegalArgumentException",
1432 "invalid time tag length");
1433 return;
1434 }
1435
1436 uint32_t latitude[GPS_VALUE_LENGTH];
1437 uint32_t longitude[GPS_VALUE_LENGTH];
1438 uint32_t timestamp[GPS_VALUE_LENGTH];
1439
1440 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1441 reinterpret_cast<jint*>(&latitude));
1442 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1443 reinterpret_cast<jint*>(&longitude));
1444 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH),
1445 reinterpret_cast<jint*>(&timestamp));
1446
1447 const jsize GPS_REF_LENGTH = 2;
1448 const jsize GPS_DATE_LENGTH = 11;
1449 uint8_t latitudeRef[GPS_REF_LENGTH];
1450 uint8_t longitudeRef[GPS_REF_LENGTH];
1451 uint8_t date[GPS_DATE_LENGTH];
1452
1453 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef));
1454 latitudeRef[GPS_REF_LENGTH - 1] = '\0';
1455 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef));
1456 longitudeRef[GPS_REF_LENGTH - 1] = '\0';
1457
1458 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date));
1459 date[GPS_DATE_LENGTH - 1] = '\0';
1460
1461 {
1462 uint8_t version[] = {2, 3, 0, 0};
1463 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version,
1464 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer);
1465 }
1466
1467 {
1468 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef,
1469 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer);
1470 }
1471
1472 {
1473 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef,
1474 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer);
1475 }
1476
1477 {
1478 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude,
1479 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer);
1480 }
1481
1482 {
1483 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude,
1484 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer);
1485 }
1486
1487 {
1488 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp,
1489 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer);
1490 }
1491
1492 {
1493 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date,
1494 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer);
1495 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001496}
1497
Ruben Brunk47e91f22014-05-28 18:38:42 -07001498static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width,
1499 jint height) {
1500 ALOGV("%s:", __FUNCTION__);
1501
1502 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1503 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1504 if (writer == NULL || context == NULL) {
1505 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1506 jniThrowException(env, "java/lang/AssertionError",
1507 "setThumbnail called with uninitialized DngCreator");
1508 return;
1509 }
1510
1511 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL;
1512 jlong capacity = env->GetDirectBufferCapacity(buffer);
1513 if (capacity != fullSize) {
1514 jniThrowExceptionFmt(env, "java/lang/AssertionError",
1515 "Invalid size %d for thumbnail, expected size was %d",
1516 capacity, fullSize);
1517 return;
1518 }
1519
1520 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
1521 if (pixelBytes == NULL) {
1522 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1523 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1524 return;
1525 }
1526
1527 if (!writer->hasIfd(TIFF_IFD_SUB1)) {
1528 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) {
1529 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1,
1530 TIFF_IFD_0);
1531 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD");
1532 return;
1533 }
1534
1535 Vector<uint16_t> tagsToMove;
1536 tagsToMove.add(TAG_ORIENTATION);
1537 tagsToMove.add(TAG_NEWSUBFILETYPE);
1538 tagsToMove.add(TAG_BITSPERSAMPLE);
1539 tagsToMove.add(TAG_COMPRESSION);
1540 tagsToMove.add(TAG_IMAGEWIDTH);
1541 tagsToMove.add(TAG_IMAGELENGTH);
1542 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
1543 tagsToMove.add(TAG_BLACKLEVEL);
1544 tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
1545 tagsToMove.add(TAG_SAMPLESPERPIXEL);
1546 tagsToMove.add(TAG_PLANARCONFIGURATION);
1547 tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
1548 tagsToMove.add(TAG_CFAPATTERN);
1549 tagsToMove.add(TAG_CFAPLANECOLOR);
1550 tagsToMove.add(TAG_CFALAYOUT);
1551 tagsToMove.add(TAG_XRESOLUTION);
1552 tagsToMove.add(TAG_YRESOLUTION);
1553 tagsToMove.add(TAG_RESOLUTIONUNIT);
1554 tagsToMove.add(TAG_WHITELEVEL);
1555 tagsToMove.add(TAG_DEFAULTSCALE);
1556 tagsToMove.add(TAG_ROWSPERSTRIP);
1557 tagsToMove.add(TAG_STRIPBYTECOUNTS);
1558 tagsToMove.add(TAG_STRIPOFFSETS);
1559 tagsToMove.add(TAG_DEFAULTCROPORIGIN);
1560 tagsToMove.add(TAG_DEFAULTCROPSIZE);
1561 tagsToMove.add(TAG_OPCODELIST2);
1562
1563 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
1564 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
1565 return;
1566 }
1567
1568 // Make sure both IFDs get the same orientation tag
1569 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
1570 if (orientEntry != NULL) {
1571 writer->addEntry(orientEntry, TIFF_IFD_0);
1572 }
1573 }
1574
1575 // Setup thumbnail tags
1576
1577 {
1578 // Set photometric interpretation
1579 uint16_t interpretation = 2; // RGB
1580 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation,
1581 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
1582 }
1583
1584 {
1585 // Set planar configuration
1586 uint16_t config = 1; // Chunky
1587 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0),
1588 env, TAG_PLANARCONFIGURATION, writer);
1589 }
1590
1591 {
1592 // Set samples per pixel
1593 uint16_t samples = SAMPLES_PER_RGB_PIXEL;
1594 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0),
1595 env, TAG_SAMPLESPERPIXEL, writer);
1596 }
1597
1598 {
1599 // Set bits per sample
1600 uint16_t bits = BITS_PER_RGB_SAMPLE;
1601 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env,
1602 TAG_BITSPERSAMPLE, writer);
1603 }
1604
1605 {
1606 // Set subfiletype
1607 uint32_t subfileType = 1; // Thumbnail image
1608 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env,
1609 TAG_NEWSUBFILETYPE, writer);
1610 }
1611
1612 {
1613 // Set compression
1614 uint16_t compression = 1; // None
1615 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env,
1616 TAG_COMPRESSION, writer);
1617 }
1618
1619 {
1620 // Set dimensions
1621 uint32_t uWidth = static_cast<uint32_t>(width);
1622 uint32_t uHeight = static_cast<uint32_t>(height);
1623 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env,
1624 TAG_IMAGEWIDTH, writer);
1625 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env,
1626 TAG_IMAGELENGTH, writer);
1627 }
1628
1629 {
1630 // x resolution
1631 uint32_t xres[] = { 72, 1 }; // default 72 ppi
1632 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
1633 env, TAG_XRESOLUTION, writer);
1634
1635 // y resolution
1636 uint32_t yres[] = { 72, 1 }; // default 72 ppi
1637 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
1638 env, TAG_YRESOLUTION, writer);
1639
1640 uint16_t unit = 2; // inches
1641 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
1642 env, TAG_RESOLUTIONUNIT, writer);
1643 }
1644
1645 {
1646 // Setup data strips
1647 if (writer->addStrip(TIFF_IFD_0) != OK) {
1648 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
1649 jniThrowException(env, "java/lang/IllegalStateException",
1650 "Failed to setup thumbnail strip tags.");
1651 return;
1652 }
1653 if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
1654 ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
1655 jniThrowException(env, "java/lang/IllegalStateException",
1656 "Failed to setup main image strip tags.");
1657 return;
1658 }
1659 }
1660
1661 if (!context->setThumbnail(pixelBytes, width, height)) {
1662 jniThrowException(env, "java/lang/IllegalStateException",
1663 "Failed to set thumbnail.");
1664 return;
1665 }
1666}
1667
1668// TODO: Refactor out common preamble for the two nativeWrite methods.
Ruben Brunkf967a542014-04-28 16:31:11 -07001669static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001670 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset,
1671 jboolean isDirect) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001672 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001673 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d,"
1674 " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset);
1675 uint32_t rStride = static_cast<uint32_t>(rowStride);
1676 uint32_t pStride = static_cast<uint32_t>(pixStride);
1677 uint32_t uWidth = static_cast<uint32_t>(width);
1678 uint32_t uHeight = static_cast<uint32_t>(height);
1679 uint64_t uOffset = static_cast<uint64_t>(offset);
Ruben Brunkf967a542014-04-28 16:31:11 -07001680
1681 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1682 if(env->ExceptionCheck()) {
1683 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1684 return;
1685 }
1686
Ruben Brunkf967a542014-04-28 16:31:11 -07001687 TiffWriter* writer = DngCreator_getCreator(env, thiz);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001688 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1689 if (writer == NULL || context == NULL) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001690 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1691 jniThrowException(env, "java/lang/AssertionError",
1692 "Write called with uninitialized DngCreator");
1693 return;
1694 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001695
1696 // Validate DNG header
1697 if (!validateDngHeader(env, writer, width, height)) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001698 return;
1699 }
1700
Ruben Brunk47e91f22014-05-28 18:38:42 -07001701 sp<JniInputByteBuffer> inBuf;
1702 Vector<StripSource*> sources;
1703 sp<DirectStripSource> thumbnailSource;
1704 uint32_t targetIfd = TIFF_IFD_0;
1705
1706 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1707
1708 if (hasThumbnail) {
1709 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1710 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1711 uint32_t thumbWidth = context->getThumbnailWidth();
1712 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1713 thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
1714 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1715 SAMPLES_PER_RGB_PIXEL);
1716 sources.add(thumbnailSource.get());
1717 targetIfd = TIFF_IFD_SUB1;
Ruben Brunkf967a542014-04-28 16:31:11 -07001718 }
1719
Ruben Brunk47e91f22014-05-28 18:38:42 -07001720 if (isDirect) {
1721 size_t fullSize = rStride * uHeight;
1722 jlong capacity = env->GetDirectBufferCapacity(inBuffer);
1723 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) {
1724 jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
1725 "Invalid size %d for Image, size given in metadata is %d at current stride",
1726 capacity, fullSize);
1727 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001728 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001729
Ruben Brunk47e91f22014-05-28 18:38:42 -07001730 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer));
1731 if (pixelBytes == NULL) {
1732 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__);
1733 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer");
1734 return;
1735 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001736
Ruben Brunk47e91f22014-05-28 18:38:42 -07001737 ALOGV("%s: Using direct-type strip source.", __FUNCTION__);
1738 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
1739 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1740 sources.add(&stripSource);
1741
1742 status_t ret = OK;
1743 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1744 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001745 if (!env->ExceptionCheck()) {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001746 jniThrowExceptionFmt(env, "java/io/IOException",
1747 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001748 }
1749 return;
1750 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001751 } else {
Ruben Brunk47e91f22014-05-28 18:38:42 -07001752 inBuf = new JniInputByteBuffer(env, inBuffer);
1753
1754 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1755 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
1756 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1757 sources.add(&stripSource);
1758
1759 status_t ret = OK;
1760 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1761 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1762 if (!env->ExceptionCheck()) {
1763 jniThrowExceptionFmt(env, "java/io/IOException",
1764 "Encountered error %d while writing file.", ret);
Ruben Brunkf967a542014-04-28 16:31:11 -07001765 }
Ruben Brunk47e91f22014-05-28 18:38:42 -07001766 return;
Ruben Brunkf967a542014-04-28 16:31:11 -07001767 }
1768 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001769}
1770
1771static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
Ruben Brunk47e91f22014-05-28 18:38:42 -07001772 jobject inStream, jint width, jint height, jlong offset) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001773 ALOGV("%s:", __FUNCTION__);
Ruben Brunk47e91f22014-05-28 18:38:42 -07001774
1775 uint32_t rowStride = width * BYTES_PER_SAMPLE;
1776 uint32_t pixStride = BYTES_PER_SAMPLE;
1777 uint32_t uWidth = static_cast<uint32_t>(width);
1778 uint32_t uHeight = static_cast<uint32_t>(height);
1779 uint64_t uOffset = static_cast<uint32_t>(offset);
1780
1781 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u,"
1782 "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride,
1783 offset);
1784
1785 sp<JniOutputStream> out = new JniOutputStream(env, outStream);
1786 if(env->ExceptionCheck()) {
1787 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__);
1788 return;
1789 }
1790
1791 TiffWriter* writer = DngCreator_getCreator(env, thiz);
1792 NativeContext* context = DngCreator_getNativeContext(env, thiz);
1793 if (writer == NULL || context == NULL) {
1794 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__);
1795 jniThrowException(env, "java/lang/AssertionError",
1796 "Write called with uninitialized DngCreator");
1797 return;
1798 }
1799
1800 // Validate DNG header
1801 if (!validateDngHeader(env, writer, width, height)) {
1802 return;
1803 }
1804
1805 sp<DirectStripSource> thumbnailSource;
1806 uint32_t targetIfd = TIFF_IFD_0;
1807 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
1808 Vector<StripSource*> sources;
1809
1810 if (hasThumbnail) {
1811 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
1812 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
1813 uint32_t width = context->getThumbnailWidth();
1814 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
1815 width, context->getThumbnailHeight(), bytesPerPixel,
1816 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
1817 SAMPLES_PER_RGB_PIXEL);
1818 sources.add(thumbnailSource.get());
1819 targetIfd = TIFF_IFD_SUB1;
1820 }
1821
1822 sp<JniInputStream> in = new JniInputStream(env, inStream);
1823
1824 ALOGV("%s: Using input-type strip source.", __FUNCTION__);
1825 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride,
1826 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
1827 sources.add(&stripSource);
1828
1829 status_t ret = OK;
1830 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
1831 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
1832 if (!env->ExceptionCheck()) {
1833 jniThrowExceptionFmt(env, "java/io/IOException",
1834 "Encountered error %d while writing file.", ret);
1835 }
1836 return;
1837 }
Ruben Brunkf967a542014-04-28 16:31:11 -07001838}
1839
1840} /*extern "C" */
1841
1842static JNINativeMethod gDngCreatorMethods[] = {
1843 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
1844 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
Ruben Brunkb8df8e02014-06-02 22:59:45 -07001845 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
1846 (void*) DngCreator_init},
Ruben Brunkf967a542014-04-28 16:31:11 -07001847 {"nativeDestroy", "()V", (void*) DngCreator_destroy},
1848 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001849 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription},
1850 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V",
1851 (void*) DngCreator_nativeSetGpsTags},
1852 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail},
1853 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001854 (void*) DngCreator_nativeWriteImage},
Ruben Brunk47e91f22014-05-28 18:38:42 -07001855 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V",
Ruben Brunkf967a542014-04-28 16:31:11 -07001856 (void*) DngCreator_nativeWriteInputStream},
1857};
1858
Ruben Brunkb6079002014-05-22 12:33:54 -07001859int register_android_hardware_camera2_DngCreator(JNIEnv *env) {
Ruben Brunkf967a542014-04-28 16:31:11 -07001860 return AndroidRuntime::registerNativeMethods(env,
Ruben Brunkb6079002014-05-22 12:33:54 -07001861 "android/hardware/camera2/DngCreator", gDngCreatorMethods,
1862 NELEM(gDngCreatorMethods));
Ruben Brunkf967a542014-04-28 16:31:11 -07001863}