android_hardware_camera2_DngCreator.cpp revision 3e1902504979b9b456a14dffa6507ee2d9ea3d6a
1/* 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> 27#include <img_utils/Input.h> 28#include <img_utils/StripSource.h> 29 30#include <utils/Log.h> 31#include <utils/Errors.h> 32#include <utils/StrongPointer.h> 33#include <utils/RefBase.h> 34#include <utils/Vector.h> 35#include <cutils/properties.h> 36 37#include <string.h> 38 39#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 48#define BAIL_IF_INVALID(expr, jnienv, tagId, writer) \ 49 if ((expr) != OK) { \ 50 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 51 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 52 return; \ 53 } 54 55#define BAIL_IF_EMPTY(entry, jnienv, tagId, writer) \ 56 if (entry.count == 0) { \ 57 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 58 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 59 return; \ 60 } 61 62#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext" 63 64static struct { 65 jfieldID mNativeContext; 66} gDngCreatorClassInfo; 67 68static struct { 69 jmethodID mWriteMethod; 70} gOutputStreamClassInfo; 71 72static struct { 73 jmethodID mReadMethod; 74 jmethodID mSkipMethod; 75} gInputStreamClassInfo; 76 77static struct { 78 jmethodID mGetMethod; 79} gInputByteBufferClassInfo; 80 81enum { 82 BITS_PER_SAMPLE = 16, 83 BYTES_PER_SAMPLE = 2, 84 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, 92}; 93 94// ---------------------------------------------------------------------------- 95 96/** 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 */ 164class JniOutputStream : public Output, public LightRefBase<JniOutputStream> { 165public: 166 JniOutputStream(JNIEnv* env, jobject outStream); 167 168 virtual ~JniOutputStream(); 169 170 status_t open(); 171 172 status_t write(const uint8_t* buf, size_t offset, size_t count); 173 174 status_t close(); 175private: 176 enum { 177 BYTE_ARRAY_LENGTH = 4096 178 }; 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 229// End of JniOutputStream 230// ---------------------------------------------------------------------------- 231 232/** 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 = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel; 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 = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel; 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// ---------------------------------------------------------------------------- 657extern "C" { 658 659static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) { 660 ALOGV("%s:", __FUNCTION__); 661 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz, 662 gDngCreatorClassInfo.mNativeContext)); 663} 664 665static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) { 666 ALOGV("%s:", __FUNCTION__); 667 NativeContext* current = DngCreator_getNativeContext(env, thiz); 668 669 if (context != NULL) { 670 context->incStrong((void*) DngCreator_setNativeContext); 671 } 672 673 if (current) { 674 current->decStrong((void*) DngCreator_setNativeContext); 675 } 676 677 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext, 678 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; 688} 689 690static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) { 691 ALOGV("%s:", __FUNCTION__); 692 693 gDngCreatorClassInfo.mNativeContext = env->GetFieldID(clazz, 694 ANDROID_DNGCREATOR_CTX_JNI_ID, "J"); 695 LOG_ALWAYS_FATAL_IF(gDngCreatorClassInfo.mNativeContext == NULL, 696 "can't find android/hardware/camera2/DngCreator.%s", 697 ANDROID_DNGCREATOR_CTX_JNI_ID); 698 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"); 703 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"); 716} 717 718static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr, 719 jobject resultsPtr, jstring formattedCaptureTime) { 720 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 734 sp<NativeContext> nativeContext = new NativeContext(); 735 TiffWriter* writer = nativeContext->getWriter(); 736 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. 750 // TODO: Add remaining non-essential tags 751 752 // Setup main image tags 753 754 { 755 // Set orientation 756 uint16_t orientation = 1; // Normal 757 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env, 758 TAG_ORIENTATION, writer); 759 } 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, 765 TAG_NEWSUBFILETYPE, writer); 766 } 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, 772 TAG_BITSPERSAMPLE, writer); 773 } 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, 779 TAG_COMPRESSION, writer); 780 } 781 782 { 783 // Set dimensions 784 camera_metadata_entry entry = 785 characteristics.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE); 786 BAIL_IF_EMPTY(entry, env, TAG_IMAGEWIDTH, writer); 787 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, 790 TAG_IMAGEWIDTH, writer); 791 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &height, TIFF_IFD_0), env, 792 TAG_IMAGELENGTH, writer); 793 imageWidth = width; 794 imageHeight = height; 795 } 796 797 { 798 // Set photometric interpretation 799 uint16_t interpretation = 32803; // CFA 800 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation, 801 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); 802 } 803 804 { 805 // Set blacklevel tags 806 camera_metadata_entry entry = 807 characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN); 808 BAIL_IF_EMPTY(entry, env, TAG_BLACKLEVEL, writer); 809 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, 811 TAG_BLACKLEVEL, writer); 812 813 uint16_t repeatDim[2] = {2, 2}; 814 BAIL_IF_INVALID(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, TIFF_IFD_0), env, 815 TAG_BLACKLEVELREPEATDIM, writer); 816 } 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), 822 env, TAG_SAMPLESPERPIXEL, writer); 823 } 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), 829 env, TAG_PLANARCONFIGURATION, writer); 830 } 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), 836 env, TAG_CFAREPEATPATTERNDIM, writer); 837 } 838 839 { 840 // Set CFA pattern 841 camera_metadata_entry entry = 842 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT); 843 BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer); 844 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), 851 env, TAG_CFAPATTERN, writer); 852 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), 858 env, TAG_CFAPATTERN, writer); 859 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), 865 env, TAG_CFAPATTERN, writer); 866 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), 872 env, TAG_CFAPATTERN, writer); 873 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), 888 env, TAG_CFAPLANECOLOR, writer); 889 } 890 891 { 892 // Set CFA layout 893 uint16_t cfaLayout = 1; 894 BAIL_IF_INVALID(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0), 895 env, TAG_CFALAYOUT, writer); 896 } 897 898 { 899 // image description 900 uint8_t imageDescription = '\0'; // empty 901 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0), 902 env, TAG_IMAGEDESCRIPTION, writer); 903 } 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), 914 TIFF_IFD_0), env, TAG_MAKE, writer); 915 } 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), 926 TIFF_IFD_0), env, TAG_MODEL, writer); 927 } 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), 933 env, TAG_XRESOLUTION, writer); 934 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), 938 env, TAG_YRESOLUTION, writer); 939 940 uint16_t unit = 2; // inches 941 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), 942 env, TAG_RESOLUTIONUNIT, writer); 943 } 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), 951 TIFF_IFD_0), env, TAG_SOFTWARE, writer); 952 } 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 966 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 } 973 974 // datetime original 975 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 } 982 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, 989 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer); 990 } 991 992 { 993 // copyright 994 uint8_t copyright = '\0'; // empty 995 BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, ©right, 996 TIFF_IFD_0), env, TAG_COPYRIGHT, writer); 997 } 998 999 { 1000 // exposure time 1001 camera_metadata_entry entry = 1002 results.find(ANDROID_SENSOR_EXPOSURE_TIME); 1003 BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME, writer); 1004 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, 1029 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer); 1030 1031 } 1032 1033 { 1034 // ISO speed ratings 1035 camera_metadata_entry entry = 1036 results.find(ANDROID_SENSOR_SENSITIVITY); 1037 BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS, writer); 1038 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, 1053 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer); 1054 } 1055 1056 { 1057 // focal length 1058 camera_metadata_entry entry = 1059 results.find(ANDROID_LENS_FOCAL_LENGTH); 1060 BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH, writer); 1061 1062 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; 1063 BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength, 1064 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer); 1065 } 1066 1067 { 1068 // f number 1069 camera_metadata_entry entry = 1070 results.find(ANDROID_LENS_APERTURE); 1071 BAIL_IF_EMPTY(entry, env, TAG_FNUMBER, writer); 1072 1073 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; 1074 BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum, 1075 TIFF_IFD_0), env, TAG_FNUMBER, writer); 1076 } 1077 1078 { 1079 // 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), 1082 env, TAG_DNGVERSION, writer); 1083 1084 uint8_t backwardVersion[4] = {1, 1, 0, 0}; 1085 BAIL_IF_INVALID(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, TIFF_IFD_0), 1086 env, TAG_DNGBACKWARDVERSION, writer); 1087 } 1088 1089 { 1090 // Set whitelevel 1091 camera_metadata_entry entry = 1092 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL); 1093 BAIL_IF_EMPTY(entry, env, TAG_WHITELEVEL, writer); 1094 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, 1096 TAG_WHITELEVEL, writer); 1097 } 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), 1103 env, TAG_DEFAULTSCALE, writer); 1104 } 1105 1106 bool singleIlluminant = false; 1107 { 1108 // Set calibration illuminants 1109 camera_metadata_entry entry1 = 1110 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1); 1111 BAIL_IF_EMPTY(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer); 1112 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, 1120 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer); 1121 1122 if (!singleIlluminant) { 1123 uint16_t ref2 = entry2.data.u8[0]; 1124 BAIL_IF_INVALID(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2, 1125 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer); 1126 } 1127 } 1128 1129 { 1130 // Set color transforms 1131 camera_metadata_entry entry1 = 1132 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1); 1133 BAIL_IF_EMPTY(entry1, env, TAG_COLORMATRIX1, writer); 1134 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 1143 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX1, entry1.count, colorTransform1, 1144 TIFF_IFD_0), env, TAG_COLORMATRIX1, writer); 1145 1146 if (!singleIlluminant) { 1147 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2); 1148 BAIL_IF_EMPTY(entry2, env, TAG_COLORMATRIX2, writer); 1149 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 1157 BAIL_IF_INVALID(writer->addEntry(TAG_COLORMATRIX2, entry2.count, colorTransform2, 1158 TIFF_IFD_0), env, TAG_COLORMATRIX2, writer); 1159 } 1160 } 1161 1162 { 1163 // Set calibration transforms 1164 camera_metadata_entry entry1 = 1165 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1); 1166 BAIL_IF_EMPTY(entry1, env, TAG_CAMERACALIBRATION1, writer); 1167 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, 1177 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer); 1178 1179 if (!singleIlluminant) { 1180 camera_metadata_entry entry2 = 1181 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2); 1182 BAIL_IF_EMPTY(entry2, env, TAG_CAMERACALIBRATION2, writer); 1183 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, 1192 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer); 1193 } 1194 } 1195 1196 { 1197 // Set forward transforms 1198 camera_metadata_entry entry1 = 1199 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1); 1200 BAIL_IF_EMPTY(entry1, env, TAG_FORWARDMATRIX1, writer); 1201 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, 1211 TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer); 1212 1213 if (!singleIlluminant) { 1214 camera_metadata_entry entry2 = 1215 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2); 1216 BAIL_IF_EMPTY(entry2, env, TAG_FORWARDMATRIX2, writer); 1217 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, 1226 TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer); 1227 } 1228 } 1229 1230 { 1231 // Set camera neutral 1232 camera_metadata_entry entry = 1233 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT); 1234 BAIL_IF_EMPTY(entry, env, TAG_ASSHOTNEUTRAL, writer); 1235 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, 1246 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer); 1247 } 1248 1249 { 1250 // Setup data strips 1251 // TODO: Switch to tiled implementation. 1252 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 } 1258 } 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, 1268 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer); 1269 BAIL_IF_INVALID(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize, 1270 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer); 1271 } 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, 1293 TAG_UNIQUECAMERAMODEL, writer); 1294 } 1295 1296 { 1297 // Setup sensor noise model 1298 camera_metadata_entry entry = 1299 results.find(ANDROID_SENSOR_NOISE_PROFILE); 1300 1301 if (entry.count > 0) { 1302 BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, entry.count, 1303 entry.data.d, TIFF_IFD_0), env, 1304 TAG_NOISEPROFILE, writer); 1305 } else { 1306 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.", 1307 __FUNCTION__); 1308 } 1309 } 1310 1311 { 1312 // Setup opcode List 2 1313 camera_metadata_entry entry1 = 1314 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE); 1315 1316 uint32_t lsmWidth = 0; 1317 uint32_t lsmHeight = 0; 1318 1319 if (entry1.count != 0) { 1320 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]); 1321 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]); 1322 } 1323 1324 camera_metadata_entry entry2 = 1325 results.find(ANDROID_STATISTICS_LENS_SHADING_MAP); 1326 1327 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) { 1328 1329 OpcodeListBuilder builder; 1330 status_t err = builder.addGainMapsForMetadata(lsmWidth, 1331 lsmHeight, 1332 0, 1333 0, 1334 imageHeight, 1335 imageWidth, 1336 opcodeCfaLayout, 1337 entry2.data.f); 1338 if (err == OK) { 1339 size_t listSize = builder.getSize(); 1340 uint8_t opcodeListBuf[listSize]; 1341 err = builder.buildOpList(opcodeListBuf); 1342 if (err == OK) { 1343 BAIL_IF_INVALID(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf, 1344 TIFF_IFD_0), env, TAG_OPCODELIST2, writer); 1345 } else { 1346 ALOGE("%s: Could not build Lens shading map opcode.", __FUNCTION__); 1347 jniThrowRuntimeException(env, "failed to construct lens shading map opcode."); 1348 } 1349 } else { 1350 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__); 1351 jniThrowRuntimeException(env, "failed to add lens shading map."); 1352 } 1353 } else { 1354 ALOGW("%s: No lens shading map found in result metadata. Image quality may be reduced.", 1355 __FUNCTION__); 1356 } 1357 } 1358 1359 DngCreator_setNativeContext(env, thiz, nativeContext); 1360} 1361 1362static void DngCreator_destroy(JNIEnv* env, jobject thiz) { 1363 ALOGV("%s:", __FUNCTION__); 1364 DngCreator_setNativeContext(env, thiz, NULL); 1365} 1366 1367static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) { 1368 ALOGV("%s:", __FUNCTION__); 1369 1370 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1371 if (writer == NULL) { 1372 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1373 jniThrowException(env, "java/lang/AssertionError", 1374 "setOrientation called with uninitialized DngCreator"); 1375 return; 1376 } 1377 1378 uint16_t orientation = static_cast<uint16_t>(orient); 1379 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), env, 1380 TAG_ORIENTATION, writer); 1381 1382 // Set main image orientation also if in a separate IFD 1383 if (writer->hasIfd(TIFF_IFD_SUB1)) { 1384 BAIL_IF_INVALID(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_SUB1), env, 1385 TAG_ORIENTATION, writer); 1386 } 1387} 1388 1389static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) { 1390 ALOGV("%s:", __FUNCTION__); 1391 1392 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1393 if (writer == NULL) { 1394 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1395 jniThrowException(env, "java/lang/AssertionError", 1396 "setDescription called with uninitialized DngCreator"); 1397 return; 1398 } 1399 1400 const char* desc = env->GetStringUTFChars(description, NULL); 1401 size_t len = strlen(desc) + 1; 1402 1403 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len, 1404 reinterpret_cast<const uint8_t*>(desc), TIFF_IFD_0) != OK) { 1405 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", 1406 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION); 1407 } 1408 1409 env->ReleaseStringUTFChars(description, desc); 1410} 1411 1412static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, jstring latRef, 1413 jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) { 1414 ALOGV("%s:", __FUNCTION__); 1415 1416 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1417 if (writer == NULL) { 1418 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1419 jniThrowException(env, "java/lang/AssertionError", 1420 "setGpsTags called with uninitialized DngCreator"); 1421 return; 1422 } 1423 1424 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) { 1425 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) { 1426 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO, 1427 TIFF_IFD_0); 1428 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO"); 1429 return; 1430 } 1431 } 1432 1433 const jsize GPS_VALUE_LENGTH = 6; 1434 jsize latLen = env->GetArrayLength(latTag); 1435 jsize longLen = env->GetArrayLength(longTag); 1436 jsize timeLen = env->GetArrayLength(timeTag); 1437 if (latLen != GPS_VALUE_LENGTH) { 1438 jniThrowException(env, "java/lang/IllegalArgumentException", 1439 "invalid latitude tag length"); 1440 return; 1441 } else if (longLen != GPS_VALUE_LENGTH) { 1442 jniThrowException(env, "java/lang/IllegalArgumentException", 1443 "invalid longitude tag length"); 1444 return; 1445 } else if (timeLen != GPS_VALUE_LENGTH) { 1446 jniThrowException(env, "java/lang/IllegalArgumentException", 1447 "invalid time tag length"); 1448 return; 1449 } 1450 1451 uint32_t latitude[GPS_VALUE_LENGTH]; 1452 uint32_t longitude[GPS_VALUE_LENGTH]; 1453 uint32_t timestamp[GPS_VALUE_LENGTH]; 1454 1455 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), 1456 reinterpret_cast<jint*>(&latitude)); 1457 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), 1458 reinterpret_cast<jint*>(&longitude)); 1459 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GPS_VALUE_LENGTH), 1460 reinterpret_cast<jint*>(×tamp)); 1461 1462 const jsize GPS_REF_LENGTH = 2; 1463 const jsize GPS_DATE_LENGTH = 11; 1464 uint8_t latitudeRef[GPS_REF_LENGTH]; 1465 uint8_t longitudeRef[GPS_REF_LENGTH]; 1466 uint8_t date[GPS_DATE_LENGTH]; 1467 1468 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&latitudeRef)); 1469 latitudeRef[GPS_REF_LENGTH - 1] = '\0'; 1470 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&longitudeRef)); 1471 longitudeRef[GPS_REF_LENGTH - 1] = '\0'; 1472 1473 env->GetStringUTFRegion(dateTag, 0, GPS_DATE_LENGTH - 1, reinterpret_cast<char*>(&date)); 1474 date[GPS_DATE_LENGTH - 1] = '\0'; 1475 1476 { 1477 uint8_t version[] = {2, 3, 0, 0}; 1478 BAIL_IF_INVALID(writer->addEntry(TAG_GPSVERSIONID, 4, version, 1479 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer); 1480 } 1481 1482 { 1483 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDEREF, GPS_REF_LENGTH, latitudeRef, 1484 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDEREF, writer); 1485 } 1486 1487 { 1488 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDEREF, GPS_REF_LENGTH, longitudeRef, 1489 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDEREF, writer); 1490 } 1491 1492 { 1493 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLATITUDE, 3, latitude, 1494 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer); 1495 } 1496 1497 { 1498 BAIL_IF_INVALID(writer->addEntry(TAG_GPSLONGITUDE, 3, longitude, 1499 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer); 1500 } 1501 1502 { 1503 BAIL_IF_INVALID(writer->addEntry(TAG_GPSTIMESTAMP, 3, timestamp, 1504 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer); 1505 } 1506 1507 { 1508 BAIL_IF_INVALID(writer->addEntry(TAG_GPSDATESTAMP, GPS_DATE_LENGTH, date, 1509 TIFF_IFD_GPSINFO), env, TAG_GPSDATESTAMP, writer); 1510 } 1511} 1512 1513static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width, 1514 jint height) { 1515 ALOGV("%s:", __FUNCTION__); 1516 1517 NativeContext* context = DngCreator_getNativeContext(env, thiz); 1518 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1519 if (writer == NULL || context == NULL) { 1520 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1521 jniThrowException(env, "java/lang/AssertionError", 1522 "setThumbnail called with uninitialized DngCreator"); 1523 return; 1524 } 1525 1526 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL; 1527 jlong capacity = env->GetDirectBufferCapacity(buffer); 1528 if (capacity != fullSize) { 1529 jniThrowExceptionFmt(env, "java/lang/AssertionError", 1530 "Invalid size %d for thumbnail, expected size was %d", 1531 capacity, fullSize); 1532 return; 1533 } 1534 1535 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); 1536 if (pixelBytes == NULL) { 1537 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); 1538 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); 1539 return; 1540 } 1541 1542 if (!writer->hasIfd(TIFF_IFD_SUB1)) { 1543 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) { 1544 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1, 1545 TIFF_IFD_0); 1546 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD"); 1547 return; 1548 } 1549 1550 Vector<uint16_t> tagsToMove; 1551 tagsToMove.add(TAG_ORIENTATION); 1552 tagsToMove.add(TAG_NEWSUBFILETYPE); 1553 tagsToMove.add(TAG_BITSPERSAMPLE); 1554 tagsToMove.add(TAG_COMPRESSION); 1555 tagsToMove.add(TAG_IMAGEWIDTH); 1556 tagsToMove.add(TAG_IMAGELENGTH); 1557 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION); 1558 tagsToMove.add(TAG_BLACKLEVEL); 1559 tagsToMove.add(TAG_BLACKLEVELREPEATDIM); 1560 tagsToMove.add(TAG_SAMPLESPERPIXEL); 1561 tagsToMove.add(TAG_PLANARCONFIGURATION); 1562 tagsToMove.add(TAG_CFAREPEATPATTERNDIM); 1563 tagsToMove.add(TAG_CFAPATTERN); 1564 tagsToMove.add(TAG_CFAPLANECOLOR); 1565 tagsToMove.add(TAG_CFALAYOUT); 1566 tagsToMove.add(TAG_XRESOLUTION); 1567 tagsToMove.add(TAG_YRESOLUTION); 1568 tagsToMove.add(TAG_RESOLUTIONUNIT); 1569 tagsToMove.add(TAG_WHITELEVEL); 1570 tagsToMove.add(TAG_DEFAULTSCALE); 1571 tagsToMove.add(TAG_ROWSPERSTRIP); 1572 tagsToMove.add(TAG_STRIPBYTECOUNTS); 1573 tagsToMove.add(TAG_STRIPOFFSETS); 1574 tagsToMove.add(TAG_DEFAULTCROPORIGIN); 1575 tagsToMove.add(TAG_DEFAULTCROPSIZE); 1576 tagsToMove.add(TAG_OPCODELIST2); 1577 1578 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) { 1579 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries"); 1580 return; 1581 } 1582 1583 // Make sure both IFDs get the same orientation tag 1584 sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1); 1585 if (orientEntry != NULL) { 1586 writer->addEntry(orientEntry, TIFF_IFD_0); 1587 } 1588 } 1589 1590 // Setup thumbnail tags 1591 1592 { 1593 // Set photometric interpretation 1594 uint16_t interpretation = 2; // RGB 1595 BAIL_IF_INVALID(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, &interpretation, 1596 TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); 1597 } 1598 1599 { 1600 // Set planar configuration 1601 uint16_t config = 1; // Chunky 1602 BAIL_IF_INVALID(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, TIFF_IFD_0), 1603 env, TAG_PLANARCONFIGURATION, writer); 1604 } 1605 1606 { 1607 // Set samples per pixel 1608 uint16_t samples = SAMPLES_PER_RGB_PIXEL; 1609 BAIL_IF_INVALID(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0), 1610 env, TAG_SAMPLESPERPIXEL, writer); 1611 } 1612 1613 { 1614 // Set bits per sample 1615 uint16_t bits = BITS_PER_RGB_SAMPLE; 1616 BAIL_IF_INVALID(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env, 1617 TAG_BITSPERSAMPLE, writer); 1618 } 1619 1620 { 1621 // Set subfiletype 1622 uint32_t subfileType = 1; // Thumbnail image 1623 BAIL_IF_INVALID(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, TIFF_IFD_0), env, 1624 TAG_NEWSUBFILETYPE, writer); 1625 } 1626 1627 { 1628 // Set compression 1629 uint16_t compression = 1; // None 1630 BAIL_IF_INVALID(writer->addEntry(TAG_COMPRESSION, 1, &compression, TIFF_IFD_0), env, 1631 TAG_COMPRESSION, writer); 1632 } 1633 1634 { 1635 // Set dimensions 1636 uint32_t uWidth = static_cast<uint32_t>(width); 1637 uint32_t uHeight = static_cast<uint32_t>(height); 1638 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), env, 1639 TAG_IMAGEWIDTH, writer); 1640 BAIL_IF_INVALID(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), env, 1641 TAG_IMAGELENGTH, writer); 1642 } 1643 1644 { 1645 // x resolution 1646 uint32_t xres[] = { 72, 1 }; // default 72 ppi 1647 BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), 1648 env, TAG_XRESOLUTION, writer); 1649 1650 // y resolution 1651 uint32_t yres[] = { 72, 1 }; // default 72 ppi 1652 BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), 1653 env, TAG_YRESOLUTION, writer); 1654 1655 uint16_t unit = 2; // inches 1656 BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), 1657 env, TAG_RESOLUTIONUNIT, writer); 1658 } 1659 1660 { 1661 // Setup data strips 1662 if (writer->addStrip(TIFF_IFD_0) != OK) { 1663 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__); 1664 jniThrowException(env, "java/lang/IllegalStateException", 1665 "Failed to setup thumbnail strip tags."); 1666 return; 1667 } 1668 if (writer->addStrip(TIFF_IFD_SUB1) != OK) { 1669 ALOGE("%s: Could not main image strip tags.", __FUNCTION__); 1670 jniThrowException(env, "java/lang/IllegalStateException", 1671 "Failed to setup main image strip tags."); 1672 return; 1673 } 1674 } 1675 1676 if (!context->setThumbnail(pixelBytes, width, height)) { 1677 jniThrowException(env, "java/lang/IllegalStateException", 1678 "Failed to set thumbnail."); 1679 return; 1680 } 1681} 1682 1683// TODO: Refactor out common preamble for the two nativeWrite methods. 1684static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width, 1685 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset, 1686 jboolean isDirect) { 1687 ALOGV("%s:", __FUNCTION__); 1688 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, rowStride=%d, pixStride=%d," 1689 " offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, offset); 1690 uint32_t rStride = static_cast<uint32_t>(rowStride); 1691 uint32_t pStride = static_cast<uint32_t>(pixStride); 1692 uint32_t uWidth = static_cast<uint32_t>(width); 1693 uint32_t uHeight = static_cast<uint32_t>(height); 1694 uint64_t uOffset = static_cast<uint64_t>(offset); 1695 1696 sp<JniOutputStream> out = new JniOutputStream(env, outStream); 1697 if(env->ExceptionCheck()) { 1698 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__); 1699 return; 1700 } 1701 1702 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1703 NativeContext* context = DngCreator_getNativeContext(env, thiz); 1704 if (writer == NULL || context == NULL) { 1705 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1706 jniThrowException(env, "java/lang/AssertionError", 1707 "Write called with uninitialized DngCreator"); 1708 return; 1709 } 1710 1711 // Validate DNG header 1712 if (!validateDngHeader(env, writer, width, height)) { 1713 return; 1714 } 1715 1716 sp<JniInputByteBuffer> inBuf; 1717 Vector<StripSource*> sources; 1718 sp<DirectStripSource> thumbnailSource; 1719 uint32_t targetIfd = TIFF_IFD_0; 1720 1721 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1); 1722 1723 if (hasThumbnail) { 1724 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__); 1725 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE; 1726 uint32_t thumbWidth = context->getThumbnailWidth(); 1727 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0, 1728 thumbWidth, context->getThumbnailHeight(), bytesPerPixel, 1729 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE, 1730 SAMPLES_PER_RGB_PIXEL); 1731 sources.add(thumbnailSource.get()); 1732 targetIfd = TIFF_IFD_SUB1; 1733 } 1734 1735 if (isDirect) { 1736 size_t fullSize = rStride * uHeight; 1737 jlong capacity = env->GetDirectBufferCapacity(inBuffer); 1738 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) { 1739 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", 1740 "Invalid size %d for Image, size given in metadata is %d at current stride", 1741 capacity, fullSize); 1742 return; 1743 } 1744 1745 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer)); 1746 if (pixelBytes == NULL) { 1747 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); 1748 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); 1749 return; 1750 } 1751 1752 ALOGV("%s: Using direct-type strip source.", __FUNCTION__); 1753 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride, 1754 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 1755 sources.add(&stripSource); 1756 1757 status_t ret = OK; 1758 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 1759 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 1760 if (!env->ExceptionCheck()) { 1761 jniThrowExceptionFmt(env, "java/io/IOException", 1762 "Encountered error %d while writing file.", ret); 1763 } 1764 return; 1765 } 1766 } else { 1767 inBuf = new JniInputByteBuffer(env, inBuffer); 1768 1769 ALOGV("%s: Using input-type strip source.", __FUNCTION__); 1770 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride, 1771 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 1772 sources.add(&stripSource); 1773 1774 status_t ret = OK; 1775 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 1776 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 1777 if (!env->ExceptionCheck()) { 1778 jniThrowExceptionFmt(env, "java/io/IOException", 1779 "Encountered error %d while writing file.", ret); 1780 } 1781 return; 1782 } 1783 } 1784} 1785 1786static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream, 1787 jobject inStream, jint width, jint height, jlong offset) { 1788 ALOGV("%s:", __FUNCTION__); 1789 1790 uint32_t rowStride = width * BYTES_PER_SAMPLE; 1791 uint32_t pixStride = BYTES_PER_SAMPLE; 1792 uint32_t uWidth = static_cast<uint32_t>(width); 1793 uint32_t uHeight = static_cast<uint32_t>(height); 1794 uint64_t uOffset = static_cast<uint32_t>(offset); 1795 1796 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, rowStride=%u," 1797 "pixStride=%u, offset=%lld", __FUNCTION__, width, height, rowStride, pixStride, 1798 offset); 1799 1800 sp<JniOutputStream> out = new JniOutputStream(env, outStream); 1801 if(env->ExceptionCheck()) { 1802 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__); 1803 return; 1804 } 1805 1806 TiffWriter* writer = DngCreator_getCreator(env, thiz); 1807 NativeContext* context = DngCreator_getNativeContext(env, thiz); 1808 if (writer == NULL || context == NULL) { 1809 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 1810 jniThrowException(env, "java/lang/AssertionError", 1811 "Write called with uninitialized DngCreator"); 1812 return; 1813 } 1814 1815 // Validate DNG header 1816 if (!validateDngHeader(env, writer, width, height)) { 1817 return; 1818 } 1819 1820 sp<DirectStripSource> thumbnailSource; 1821 uint32_t targetIfd = TIFF_IFD_0; 1822 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1); 1823 Vector<StripSource*> sources; 1824 1825 if (hasThumbnail) { 1826 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__); 1827 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE; 1828 uint32_t width = context->getThumbnailWidth(); 1829 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0, 1830 width, context->getThumbnailHeight(), bytesPerPixel, 1831 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE, 1832 SAMPLES_PER_RGB_PIXEL); 1833 sources.add(thumbnailSource.get()); 1834 targetIfd = TIFF_IFD_SUB1; 1835 } 1836 1837 sp<JniInputStream> in = new JniInputStream(env, inStream); 1838 1839 ALOGV("%s: Using input-type strip source.", __FUNCTION__); 1840 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride, 1841 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 1842 sources.add(&stripSource); 1843 1844 status_t ret = OK; 1845 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 1846 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 1847 if (!env->ExceptionCheck()) { 1848 jniThrowExceptionFmt(env, "java/io/IOException", 1849 "Encountered error %d while writing file.", ret); 1850 } 1851 return; 1852 } 1853} 1854 1855} /*extern "C" */ 1856 1857static JNINativeMethod gDngCreatorMethods[] = { 1858 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit}, 1859 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;" 1860 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V", 1861 (void*) DngCreator_init}, 1862 {"nativeDestroy", "()V", (void*) DngCreator_destroy}, 1863 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation}, 1864 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription}, 1865 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V", 1866 (void*) DngCreator_nativeSetGpsTags}, 1867 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail}, 1868 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V", 1869 (void*) DngCreator_nativeWriteImage}, 1870 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V", 1871 (void*) DngCreator_nativeWriteInputStream}, 1872}; 1873 1874int register_android_hardware_camera2_DngCreator(JNIEnv *env) { 1875 return AndroidRuntime::registerNativeMethods(env, 1876 "android/hardware/camera2/DngCreator", gDngCreatorMethods, 1877 NELEM(gDngCreatorMethods)); 1878} 1879