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 #include <inttypes.h> 20 #include <string.h> 21 #include <algorithm> 22 #include <memory> 23 #include <vector> 24 #include <cmath> 25 26 #include <android-base/properties.h> 27 #include <utils/Log.h> 28 #include <utils/Errors.h> 29 #include <utils/StrongPointer.h> 30 #include <utils/RefBase.h> 31 #include <utils/Vector.h> 32 #include <utils/String8.h> 33 #include <system/camera_metadata.h> 34 #include <camera/CameraMetadata.h> 35 #include <img_utils/DngUtils.h> 36 #include <img_utils/TagDefinitions.h> 37 #include <img_utils/TiffIfd.h> 38 #include <img_utils/TiffWriter.h> 39 #include <img_utils/Output.h> 40 #include <img_utils/Input.h> 41 #include <img_utils/StripSource.h> 42 43 #include "core_jni_helpers.h" 44 45 #include "android_runtime/AndroidRuntime.h" 46 #include "android_runtime/android_hardware_camera2_CameraMetadata.h" 47 48 #include <jni.h> 49 #include <nativehelper/JNIHelp.h> 50 51 using namespace android; 52 using namespace img_utils; 53 using android::base::GetProperty; 54 55 #define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \ 56 if ((expr) != OK) { \ 57 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 58 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 59 return false; \ 60 } 61 62 63 #define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \ 64 if ((expr) != OK) { \ 65 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 66 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 67 return nullptr; \ 68 } 69 70 71 #define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \ 72 if ((expr) != OK) { \ 73 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 74 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 75 return -1; \ 76 } 77 78 #define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \ 79 if ((entry).count == 0) { \ 80 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 81 "Missing metadata fields for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 82 return nullptr; \ 83 } 84 85 #define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \ 86 if (expr) { \ 87 jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \ 88 "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \ 89 return nullptr; \ 90 } 91 92 93 #define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext" 94 95 static struct { 96 jfieldID mNativeContext; 97 } gDngCreatorClassInfo; 98 99 static struct { 100 jmethodID mWriteMethod; 101 } gOutputStreamClassInfo; 102 103 static struct { 104 jmethodID mReadMethod; 105 jmethodID mSkipMethod; 106 } gInputStreamClassInfo; 107 108 static struct { 109 jmethodID mGetMethod; 110 } gInputByteBufferClassInfo; 111 112 enum { 113 BITS_PER_SAMPLE = 16, 114 BYTES_PER_SAMPLE = 2, 115 BYTES_PER_RGB_PIXEL = 3, 116 BITS_PER_RGB_SAMPLE = 8, 117 BYTES_PER_RGB_SAMPLE = 1, 118 SAMPLES_PER_RGB_PIXEL = 3, 119 SAMPLES_PER_RAW_PIXEL = 1, 120 TIFF_IFD_0 = 0, 121 TIFF_IFD_SUB1 = 1, 122 TIFF_IFD_GPSINFO = 2, 123 }; 124 125 126 /** 127 * POD container class for GPS tag data. 128 */ 129 class GpsData { 130 public: 131 enum { 132 GPS_VALUE_LENGTH = 6, 133 GPS_REF_LENGTH = 2, 134 GPS_DATE_LENGTH = 11, 135 }; 136 137 uint32_t mLatitude[GPS_VALUE_LENGTH]; 138 uint32_t mLongitude[GPS_VALUE_LENGTH]; 139 uint32_t mTimestamp[GPS_VALUE_LENGTH]; 140 uint8_t mLatitudeRef[GPS_REF_LENGTH]; 141 uint8_t mLongitudeRef[GPS_REF_LENGTH]; 142 uint8_t mDate[GPS_DATE_LENGTH]; 143 }; 144 145 // ---------------------------------------------------------------------------- 146 147 /** 148 * Container class for the persistent native context. 149 */ 150 151 class NativeContext : public LightRefBase<NativeContext> { 152 public: 153 enum { 154 DATETIME_COUNT = 20, 155 }; 156 157 NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result); 158 virtual ~NativeContext(); 159 160 TiffWriter* getWriter(); 161 162 std::shared_ptr<const CameraMetadata> getCharacteristics() const; 163 std::shared_ptr<const CameraMetadata> getResult() const; 164 165 uint32_t getThumbnailWidth() const; 166 uint32_t getThumbnailHeight() const; 167 const uint8_t* getThumbnail() const; 168 bool hasThumbnail() const; 169 170 bool setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height); 171 172 void setOrientation(uint16_t orientation); 173 uint16_t getOrientation() const; 174 175 void setDescription(const String8& desc); 176 String8 getDescription() const; 177 bool hasDescription() const; 178 179 void setGpsData(const GpsData& data); 180 GpsData getGpsData() const; 181 bool hasGpsData() const; 182 183 void setCaptureTime(const String8& formattedCaptureTime); 184 String8 getCaptureTime() const; 185 bool hasCaptureTime() const; 186 187 private: 188 Vector<uint8_t> mCurrentThumbnail; 189 TiffWriter mWriter; 190 std::shared_ptr<CameraMetadata> mCharacteristics; 191 std::shared_ptr<CameraMetadata> mResult; 192 uint32_t mThumbnailWidth; 193 uint32_t mThumbnailHeight; 194 uint16_t mOrientation; 195 bool mThumbnailSet; 196 bool mGpsSet; 197 bool mDescriptionSet; 198 bool mCaptureTimeSet; 199 String8 mDescription; 200 GpsData mGpsData; 201 String8 mFormattedCaptureTime; 202 }; 203 204 NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) : 205 mCharacteristics(std::make_shared<CameraMetadata>(characteristics)), 206 mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0), 207 mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false), 208 mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {} 209 210 NativeContext::~NativeContext() {} 211 212 TiffWriter* NativeContext::getWriter() { 213 return &mWriter; 214 } 215 216 std::shared_ptr<const CameraMetadata> NativeContext::getCharacteristics() const { 217 return mCharacteristics; 218 } 219 220 std::shared_ptr<const CameraMetadata> NativeContext::getResult() const { 221 return mResult; 222 } 223 224 uint32_t NativeContext::getThumbnailWidth() const { 225 return mThumbnailWidth; 226 } 227 228 uint32_t NativeContext::getThumbnailHeight() const { 229 return mThumbnailHeight; 230 } 231 232 const uint8_t* NativeContext::getThumbnail() const { 233 return mCurrentThumbnail.array(); 234 } 235 236 bool NativeContext::hasThumbnail() const { 237 return mThumbnailSet; 238 } 239 240 bool NativeContext::setThumbnail(const uint8_t* buffer, uint32_t width, uint32_t height) { 241 mThumbnailWidth = width; 242 mThumbnailHeight = height; 243 244 size_t size = BYTES_PER_RGB_PIXEL * width * height; 245 if (mCurrentThumbnail.resize(size) < 0) { 246 ALOGE("%s: Could not resize thumbnail buffer.", __FUNCTION__); 247 return false; 248 } 249 250 uint8_t* thumb = mCurrentThumbnail.editArray(); 251 memcpy(thumb, buffer, size); 252 mThumbnailSet = true; 253 return true; 254 } 255 256 void NativeContext::setOrientation(uint16_t orientation) { 257 mOrientation = orientation; 258 } 259 260 uint16_t NativeContext::getOrientation() const { 261 return mOrientation; 262 } 263 264 void NativeContext::setDescription(const String8& desc) { 265 mDescription = desc; 266 mDescriptionSet = true; 267 } 268 269 String8 NativeContext::getDescription() const { 270 return mDescription; 271 } 272 273 bool NativeContext::hasDescription() const { 274 return mDescriptionSet; 275 } 276 277 void NativeContext::setGpsData(const GpsData& data) { 278 mGpsData = data; 279 mGpsSet = true; 280 } 281 282 GpsData NativeContext::getGpsData() const { 283 return mGpsData; 284 } 285 286 bool NativeContext::hasGpsData() const { 287 return mGpsSet; 288 } 289 290 void NativeContext::setCaptureTime(const String8& formattedCaptureTime) { 291 mFormattedCaptureTime = formattedCaptureTime; 292 mCaptureTimeSet = true; 293 } 294 295 String8 NativeContext::getCaptureTime() const { 296 return mFormattedCaptureTime; 297 } 298 299 bool NativeContext::hasCaptureTime() const { 300 return mCaptureTimeSet; 301 } 302 303 // End of NativeContext 304 // ---------------------------------------------------------------------------- 305 306 /** 307 * Wrapper class for a Java OutputStream. 308 * 309 * This class is not intended to be used across JNI calls. 310 */ 311 class JniOutputStream : public Output, public LightRefBase<JniOutputStream> { 312 public: 313 JniOutputStream(JNIEnv* env, jobject outStream); 314 315 virtual ~JniOutputStream(); 316 317 status_t open(); 318 319 status_t write(const uint8_t* buf, size_t offset, size_t count); 320 321 status_t close(); 322 private: 323 enum { 324 BYTE_ARRAY_LENGTH = 4096 325 }; 326 jobject mOutputStream; 327 JNIEnv* mEnv; 328 jbyteArray mByteArray; 329 }; 330 331 JniOutputStream::JniOutputStream(JNIEnv* env, jobject outStream) : mOutputStream(outStream), 332 mEnv(env) { 333 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); 334 if (mByteArray == nullptr) { 335 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); 336 } 337 } 338 339 JniOutputStream::~JniOutputStream() { 340 mEnv->DeleteLocalRef(mByteArray); 341 } 342 343 status_t JniOutputStream::open() { 344 // Do nothing 345 return OK; 346 } 347 348 status_t JniOutputStream::write(const uint8_t* buf, size_t offset, size_t count) { 349 while(count > 0) { 350 size_t len = BYTE_ARRAY_LENGTH; 351 len = (count > len) ? len : count; 352 mEnv->SetByteArrayRegion(mByteArray, 0, len, reinterpret_cast<const jbyte*>(buf + offset)); 353 354 if (mEnv->ExceptionCheck()) { 355 return BAD_VALUE; 356 } 357 358 mEnv->CallVoidMethod(mOutputStream, gOutputStreamClassInfo.mWriteMethod, mByteArray, 359 0, len); 360 361 if (mEnv->ExceptionCheck()) { 362 return BAD_VALUE; 363 } 364 365 count -= len; 366 offset += len; 367 } 368 return OK; 369 } 370 371 status_t JniOutputStream::close() { 372 // Do nothing 373 return OK; 374 } 375 376 // End of JniOutputStream 377 // ---------------------------------------------------------------------------- 378 379 /** 380 * Wrapper class for a Java InputStream. 381 * 382 * This class is not intended to be used across JNI calls. 383 */ 384 class JniInputStream : public Input, public LightRefBase<JniInputStream> { 385 public: 386 JniInputStream(JNIEnv* env, jobject inStream); 387 388 status_t open(); 389 390 status_t close(); 391 392 ssize_t read(uint8_t* buf, size_t offset, size_t count); 393 394 ssize_t skip(size_t count); 395 396 virtual ~JniInputStream(); 397 private: 398 enum { 399 BYTE_ARRAY_LENGTH = 4096 400 }; 401 jobject mInStream; 402 JNIEnv* mEnv; 403 jbyteArray mByteArray; 404 405 }; 406 407 JniInputStream::JniInputStream(JNIEnv* env, jobject inStream) : mInStream(inStream), mEnv(env) { 408 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); 409 if (mByteArray == nullptr) { 410 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); 411 } 412 } 413 414 JniInputStream::~JniInputStream() { 415 mEnv->DeleteLocalRef(mByteArray); 416 } 417 418 ssize_t JniInputStream::read(uint8_t* buf, size_t offset, size_t count) { 419 420 jint realCount = BYTE_ARRAY_LENGTH; 421 if (count < BYTE_ARRAY_LENGTH) { 422 realCount = count; 423 } 424 jint actual = mEnv->CallIntMethod(mInStream, gInputStreamClassInfo.mReadMethod, mByteArray, 0, 425 realCount); 426 427 if (actual < 0) { 428 return NOT_ENOUGH_DATA; 429 } 430 431 if (mEnv->ExceptionCheck()) { 432 return BAD_VALUE; 433 } 434 435 mEnv->GetByteArrayRegion(mByteArray, 0, actual, reinterpret_cast<jbyte*>(buf + offset)); 436 if (mEnv->ExceptionCheck()) { 437 return BAD_VALUE; 438 } 439 return actual; 440 } 441 442 ssize_t JniInputStream::skip(size_t count) { 443 jlong actual = mEnv->CallLongMethod(mInStream, gInputStreamClassInfo.mSkipMethod, 444 static_cast<jlong>(count)); 445 446 if (mEnv->ExceptionCheck()) { 447 return BAD_VALUE; 448 } 449 if (actual < 0) { 450 return NOT_ENOUGH_DATA; 451 } 452 return actual; 453 } 454 455 status_t JniInputStream::open() { 456 // Do nothing 457 return OK; 458 } 459 460 status_t JniInputStream::close() { 461 // Do nothing 462 return OK; 463 } 464 465 // End of JniInputStream 466 // ---------------------------------------------------------------------------- 467 468 /** 469 * Wrapper class for a non-direct Java ByteBuffer. 470 * 471 * This class is not intended to be used across JNI calls. 472 */ 473 class JniInputByteBuffer : public Input, public LightRefBase<JniInputByteBuffer> { 474 public: 475 JniInputByteBuffer(JNIEnv* env, jobject inBuf); 476 477 status_t open(); 478 479 status_t close(); 480 481 ssize_t read(uint8_t* buf, size_t offset, size_t count); 482 483 virtual ~JniInputByteBuffer(); 484 private: 485 enum { 486 BYTE_ARRAY_LENGTH = 4096 487 }; 488 jobject mInBuf; 489 JNIEnv* mEnv; 490 jbyteArray mByteArray; 491 }; 492 493 JniInputByteBuffer::JniInputByteBuffer(JNIEnv* env, jobject inBuf) : mInBuf(inBuf), mEnv(env) { 494 mByteArray = env->NewByteArray(BYTE_ARRAY_LENGTH); 495 if (mByteArray == nullptr) { 496 jniThrowException(env, "java/lang/OutOfMemoryError", "Could not allocate byte array."); 497 } 498 } 499 500 JniInputByteBuffer::~JniInputByteBuffer() { 501 mEnv->DeleteLocalRef(mByteArray); 502 } 503 504 ssize_t JniInputByteBuffer::read(uint8_t* buf, size_t offset, size_t count) { 505 jint realCount = BYTE_ARRAY_LENGTH; 506 if (count < BYTE_ARRAY_LENGTH) { 507 realCount = count; 508 } 509 510 jobject chainingBuf = mEnv->CallObjectMethod(mInBuf, gInputByteBufferClassInfo.mGetMethod, 511 mByteArray, 0, realCount); 512 mEnv->DeleteLocalRef(chainingBuf); 513 514 if (mEnv->ExceptionCheck()) { 515 ALOGE("%s: Exception while reading from input into byte buffer.", __FUNCTION__); 516 return BAD_VALUE; 517 } 518 519 mEnv->GetByteArrayRegion(mByteArray, 0, realCount, reinterpret_cast<jbyte*>(buf + offset)); 520 if (mEnv->ExceptionCheck()) { 521 ALOGE("%s: Exception while reading from byte buffer.", __FUNCTION__); 522 return BAD_VALUE; 523 } 524 return realCount; 525 } 526 527 status_t JniInputByteBuffer::open() { 528 // Do nothing 529 return OK; 530 } 531 532 status_t JniInputByteBuffer::close() { 533 // Do nothing 534 return OK; 535 } 536 537 // End of JniInputByteBuffer 538 // ---------------------------------------------------------------------------- 539 540 /** 541 * StripSource subclass for Input types. 542 * 543 * This class is not intended to be used across JNI calls. 544 */ 545 546 class InputStripSource : public StripSource, public LightRefBase<InputStripSource> { 547 public: 548 InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, uint32_t height, 549 uint32_t pixStride, uint32_t rowStride, uint64_t offset, uint32_t bytesPerSample, 550 uint32_t samplesPerPixel); 551 552 virtual ~InputStripSource(); 553 554 virtual status_t writeToStream(Output& stream, uint32_t count); 555 556 virtual uint32_t getIfd() const; 557 protected: 558 uint32_t mIfd; 559 Input* mInput; 560 uint32_t mWidth; 561 uint32_t mHeight; 562 uint32_t mPixStride; 563 uint32_t mRowStride; 564 uint64_t mOffset; 565 JNIEnv* mEnv; 566 uint32_t mBytesPerSample; 567 uint32_t mSamplesPerPixel; 568 }; 569 570 InputStripSource::InputStripSource(JNIEnv* env, Input& input, uint32_t ifd, uint32_t width, 571 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset, 572 uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), mInput(&input), 573 mWidth(width), mHeight(height), mPixStride(pixStride), mRowStride(rowStride), 574 mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample), 575 mSamplesPerPixel(samplesPerPixel) {} 576 577 InputStripSource::~InputStripSource() {} 578 579 status_t InputStripSource::writeToStream(Output& stream, uint32_t count) { 580 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel; 581 jlong offset = mOffset; 582 583 if (fullSize != count) { 584 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count, 585 fullSize); 586 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write"); 587 return BAD_VALUE; 588 } 589 590 // Skip offset 591 while (offset > 0) { 592 ssize_t skipped = mInput->skip(offset); 593 if (skipped <= 0) { 594 if (skipped == NOT_ENOUGH_DATA || skipped == 0) { 595 jniThrowExceptionFmt(mEnv, "java/io/IOException", 596 "Early EOF encountered in skip, not enough pixel data for image of size %u", 597 fullSize); 598 skipped = NOT_ENOUGH_DATA; 599 } else { 600 if (!mEnv->ExceptionCheck()) { 601 jniThrowException(mEnv, "java/io/IOException", 602 "Error encountered while skip bytes in input stream."); 603 } 604 } 605 606 return skipped; 607 } 608 offset -= skipped; 609 } 610 611 Vector<uint8_t> row; 612 if (row.resize(mRowStride) < 0) { 613 jniThrowException(mEnv, "java/lang/OutOfMemoryError", "Could not allocate row vector."); 614 return BAD_VALUE; 615 } 616 617 uint8_t* rowBytes = row.editArray(); 618 619 for (uint32_t i = 0; i < mHeight; ++i) { 620 size_t rowFillAmt = 0; 621 size_t rowSize = mRowStride; 622 623 while (rowFillAmt < mRowStride) { 624 ssize_t bytesRead = mInput->read(rowBytes, rowFillAmt, rowSize); 625 if (bytesRead <= 0) { 626 if (bytesRead == NOT_ENOUGH_DATA || bytesRead == 0) { 627 ALOGE("%s: Early EOF on row %" PRIu32 ", received bytesRead %zd", 628 __FUNCTION__, i, bytesRead); 629 jniThrowExceptionFmt(mEnv, "java/io/IOException", 630 "Early EOF encountered, not enough pixel data for image of size %" 631 PRIu32, fullSize); 632 bytesRead = NOT_ENOUGH_DATA; 633 } else { 634 if (!mEnv->ExceptionCheck()) { 635 jniThrowException(mEnv, "java/io/IOException", 636 "Error encountered while reading"); 637 } 638 } 639 return bytesRead; 640 } 641 rowFillAmt += bytesRead; 642 rowSize -= bytesRead; 643 } 644 645 if (mPixStride == mBytesPerSample * mSamplesPerPixel) { 646 ALOGV("%s: Using stream per-row write for strip.", __FUNCTION__); 647 648 if (stream.write(rowBytes, 0, mBytesPerSample * mSamplesPerPixel * mWidth) != OK || 649 mEnv->ExceptionCheck()) { 650 if (!mEnv->ExceptionCheck()) { 651 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data"); 652 } 653 return BAD_VALUE; 654 } 655 } else { 656 ALOGV("%s: Using stream per-pixel write for strip.", __FUNCTION__); 657 jniThrowException(mEnv, "java/lang/IllegalStateException", 658 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous"); 659 return BAD_VALUE; 660 661 // TODO: Add support for non-contiguous pixels if needed. 662 } 663 } 664 return OK; 665 } 666 667 uint32_t InputStripSource::getIfd() const { 668 return mIfd; 669 } 670 671 // End of InputStripSource 672 // ---------------------------------------------------------------------------- 673 674 /** 675 * StripSource subclass for direct buffer types. 676 * 677 * This class is not intended to be used across JNI calls. 678 */ 679 680 class DirectStripSource : public StripSource, public LightRefBase<DirectStripSource> { 681 public: 682 DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, uint32_t width, 683 uint32_t height, uint32_t pixStride, uint32_t rowStride, uint64_t offset, 684 uint32_t bytesPerSample, uint32_t samplesPerPixel); 685 686 virtual ~DirectStripSource(); 687 688 virtual status_t writeToStream(Output& stream, uint32_t count); 689 690 virtual uint32_t getIfd() const; 691 protected: 692 uint32_t mIfd; 693 const uint8_t* mPixelBytes; 694 uint32_t mWidth; 695 uint32_t mHeight; 696 uint32_t mPixStride; 697 uint32_t mRowStride; 698 uint16_t mOffset; 699 JNIEnv* mEnv; 700 uint32_t mBytesPerSample; 701 uint32_t mSamplesPerPixel; 702 }; 703 704 DirectStripSource::DirectStripSource(JNIEnv* env, const uint8_t* pixelBytes, uint32_t ifd, 705 uint32_t width, uint32_t height, uint32_t pixStride, uint32_t rowStride, 706 uint64_t offset, uint32_t bytesPerSample, uint32_t samplesPerPixel) : mIfd(ifd), 707 mPixelBytes(pixelBytes), mWidth(width), mHeight(height), mPixStride(pixStride), 708 mRowStride(rowStride), mOffset(offset), mEnv(env), mBytesPerSample(bytesPerSample), 709 mSamplesPerPixel(samplesPerPixel) {} 710 711 DirectStripSource::~DirectStripSource() {} 712 713 status_t DirectStripSource::writeToStream(Output& stream, uint32_t count) { 714 uint32_t fullSize = mWidth * mHeight * mBytesPerSample * mSamplesPerPixel; 715 716 if (fullSize != count) { 717 ALOGE("%s: Amount to write %u doesn't match image size %u", __FUNCTION__, count, 718 fullSize); 719 jniThrowException(mEnv, "java/lang/IllegalStateException", "Not enough data to write"); 720 return BAD_VALUE; 721 } 722 723 724 if (mPixStride == mBytesPerSample * mSamplesPerPixel 725 && mRowStride == mWidth * mBytesPerSample * mSamplesPerPixel) { 726 ALOGV("%s: Using direct single-pass write for strip.", __FUNCTION__); 727 728 if (stream.write(mPixelBytes, mOffset, fullSize) != OK || mEnv->ExceptionCheck()) { 729 if (!mEnv->ExceptionCheck()) { 730 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data"); 731 } 732 return BAD_VALUE; 733 } 734 } else if (mPixStride == mBytesPerSample * mSamplesPerPixel) { 735 ALOGV("%s: Using direct per-row write for strip.", __FUNCTION__); 736 737 for (size_t i = 0; i < mHeight; ++i) { 738 if (stream.write(mPixelBytes, mOffset + i * mRowStride, mPixStride * mWidth) != OK || 739 mEnv->ExceptionCheck()) { 740 if (!mEnv->ExceptionCheck()) { 741 jniThrowException(mEnv, "java/io/IOException", "Failed to write pixel data"); 742 } 743 return BAD_VALUE; 744 } 745 } 746 } else { 747 ALOGV("%s: Using direct per-pixel write for strip.", __FUNCTION__); 748 749 jniThrowException(mEnv, "java/lang/IllegalStateException", 750 "Per-pixel strides are not supported for RAW16 -- pixels must be contiguous"); 751 return BAD_VALUE; 752 753 // TODO: Add support for non-contiguous pixels if needed. 754 } 755 return OK; 756 757 } 758 759 uint32_t DirectStripSource::getIfd() const { 760 return mIfd; 761 } 762 763 // End of DirectStripSource 764 // ---------------------------------------------------------------------------- 765 766 /** 767 * Calculate the default crop relative to the "active area" of the image sensor (this active area 768 * will always be the pre-correction active area rectangle), and set this. 769 */ 770 static status_t calculateAndSetCrop(JNIEnv* env, const CameraMetadata& characteristics, 771 sp<TiffWriter> writer) { 772 773 camera_metadata_ro_entry entry = 774 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 775 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); 776 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); 777 778 const uint32_t margin = 8; // Default margin recommended by Adobe for interpolation. 779 780 if (width < margin * 2 || height < margin * 2) { 781 ALOGE("%s: Cannot calculate default crop for image, pre-correction active area is too" 782 "small: h=%" PRIu32 ", w=%" PRIu32, __FUNCTION__, height, width); 783 jniThrowException(env, "java/lang/IllegalStateException", 784 "Pre-correction active area is too small."); 785 return BAD_VALUE; 786 } 787 788 uint32_t defaultCropOrigin[] = {margin, margin}; 789 uint32_t defaultCropSize[] = {width - defaultCropOrigin[0] - margin, 790 height - defaultCropOrigin[1] - margin}; 791 792 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPORIGIN, 2, defaultCropOrigin, 793 TIFF_IFD_0), env, TAG_DEFAULTCROPORIGIN, writer); 794 BAIL_IF_INVALID_R(writer->addEntry(TAG_DEFAULTCROPSIZE, 2, defaultCropSize, 795 TIFF_IFD_0), env, TAG_DEFAULTCROPSIZE, writer); 796 797 return OK; 798 } 799 800 static bool validateDngHeader(JNIEnv* env, sp<TiffWriter> writer, 801 const CameraMetadata& characteristics, jint width, jint height) { 802 if (width <= 0) { 803 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \ 804 "Image width %d is invalid", width); 805 return false; 806 } 807 808 if (height <= 0) { 809 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \ 810 "Image height %d is invalid", height); 811 return false; 812 } 813 814 camera_metadata_ro_entry preCorrectionEntry = 815 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 816 camera_metadata_ro_entry pixelArrayEntry = 817 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE); 818 819 int pWidth = static_cast<int>(pixelArrayEntry.data.i32[0]); 820 int pHeight = static_cast<int>(pixelArrayEntry.data.i32[1]); 821 int cWidth = static_cast<int>(preCorrectionEntry.data.i32[2]); 822 int cHeight = static_cast<int>(preCorrectionEntry.data.i32[3]); 823 824 bool matchesPixelArray = (pWidth == width && pHeight == height); 825 bool matchesPreCorrectionArray = (cWidth == width && cHeight == height); 826 827 if (!(matchesPixelArray || matchesPreCorrectionArray)) { 828 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", \ 829 "Image dimensions (w=%d,h=%d) are invalid, must match either the pixel " 830 "array size (w=%d, h=%d) or the pre-correction array size (w=%d, h=%d)", 831 width, height, pWidth, pHeight, cWidth, cHeight); 832 return false; 833 } 834 835 return true; 836 } 837 838 static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo, 839 const Vector<uint16_t>& entries) { 840 for (size_t i = 0; i < entries.size(); ++i) { 841 uint16_t tagId = entries[i]; 842 sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom); 843 if (entry.get() == nullptr) { 844 ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId, 845 ifdFrom); 846 return BAD_VALUE; 847 } 848 if (writer->addEntry(entry, ifdTo) != OK) { 849 ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId, 850 ifdFrom); 851 return BAD_VALUE; 852 } 853 writer->removeEntry(tagId, ifdFrom); 854 } 855 return OK; 856 } 857 858 /** 859 * Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4. 860 * Returns OK on success, or a negative error code if the CFA enum was invalid. 861 */ 862 static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) { 863 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa = 864 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>( 865 cfaEnum); 866 switch(cfa) { 867 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: { 868 cfaOut[0] = 0; 869 cfaOut[1] = 1; 870 cfaOut[2] = 1; 871 cfaOut[3] = 2; 872 break; 873 } 874 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: { 875 cfaOut[0] = 1; 876 cfaOut[1] = 0; 877 cfaOut[2] = 2; 878 cfaOut[3] = 1; 879 break; 880 } 881 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: { 882 cfaOut[0] = 1; 883 cfaOut[1] = 2; 884 cfaOut[2] = 0; 885 cfaOut[3] = 1; 886 break; 887 } 888 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: { 889 cfaOut[0] = 2; 890 cfaOut[1] = 1; 891 cfaOut[2] = 1; 892 cfaOut[3] = 0; 893 break; 894 } 895 default: { 896 return BAD_VALUE; 897 } 898 } 899 return OK; 900 } 901 902 /** 903 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to 904 * RGGB for an unknown enum. 905 */ 906 static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) { 907 camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa = 908 static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>( 909 cfaEnum); 910 switch(cfa) { 911 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: { 912 return OpcodeListBuilder::CFA_RGGB; 913 } 914 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: { 915 return OpcodeListBuilder::CFA_GRBG; 916 } 917 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: { 918 return OpcodeListBuilder::CFA_GBRG; 919 } 920 case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: { 921 return OpcodeListBuilder::CFA_BGGR; 922 } 923 default: { 924 return OpcodeListBuilder::CFA_RGGB; 925 } 926 } 927 } 928 929 /** 930 * For each color plane, find the corresponding noise profile coefficients given in the 931 * per-channel noise profile. If multiple channels in the CFA correspond to a color in the color 932 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient. 933 * 934 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients. 935 * cfa - numChannels color channels corresponding to each of the per-channel noise profile 936 * coefficients. 937 * numChannels - the number of noise profile coefficient pairs and color channels given in 938 * the perChannelNoiseProfile and cfa arguments, respectively. 939 * planeColors - the color planes in the noise profile output. 940 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile. 941 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients. 942 * 943 * returns OK, or a negative error code on failure. 944 */ 945 static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa, 946 size_t numChannels, const uint8_t* planeColors, size_t numPlanes, 947 /*out*/double* noiseProfile) { 948 949 for (size_t p = 0; p < numPlanes; ++p) { 950 size_t S = p * 2; 951 size_t O = p * 2 + 1; 952 953 noiseProfile[S] = 0; 954 noiseProfile[O] = 0; 955 bool uninitialized = true; 956 for (size_t c = 0; c < numChannels; ++c) { 957 if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) { 958 noiseProfile[S] = perChannelNoiseProfile[c * 2]; 959 noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1]; 960 uninitialized = false; 961 } 962 } 963 if (uninitialized) { 964 ALOGE("%s: No valid NoiseProfile coefficients for color plane %zu", 965 __FUNCTION__, p); 966 return BAD_VALUE; 967 } 968 } 969 return OK; 970 } 971 972 // ---------------------------------------------------------------------------- 973 extern "C" { 974 975 static NativeContext* DngCreator_getNativeContext(JNIEnv* env, jobject thiz) { 976 ALOGV("%s:", __FUNCTION__); 977 return reinterpret_cast<NativeContext*>(env->GetLongField(thiz, 978 gDngCreatorClassInfo.mNativeContext)); 979 } 980 981 static void DngCreator_setNativeContext(JNIEnv* env, jobject thiz, sp<NativeContext> context) { 982 ALOGV("%s:", __FUNCTION__); 983 NativeContext* current = DngCreator_getNativeContext(env, thiz); 984 985 if (context != nullptr) { 986 context->incStrong((void*) DngCreator_setNativeContext); 987 } 988 989 if (current) { 990 current->decStrong((void*) DngCreator_setNativeContext); 991 } 992 993 env->SetLongField(thiz, gDngCreatorClassInfo.mNativeContext, 994 reinterpret_cast<jlong>(context.get())); 995 } 996 997 static void DngCreator_nativeClassInit(JNIEnv* env, jclass clazz) { 998 ALOGV("%s:", __FUNCTION__); 999 1000 gDngCreatorClassInfo.mNativeContext = GetFieldIDOrDie(env, 1001 clazz, ANDROID_DNGCREATOR_CTX_JNI_ID, "J"); 1002 1003 jclass outputStreamClazz = FindClassOrDie(env, "java/io/OutputStream"); 1004 gOutputStreamClassInfo.mWriteMethod = GetMethodIDOrDie(env, 1005 outputStreamClazz, "write", "([BII)V"); 1006 1007 jclass inputStreamClazz = FindClassOrDie(env, "java/io/InputStream"); 1008 gInputStreamClassInfo.mReadMethod = GetMethodIDOrDie(env, inputStreamClazz, "read", "([BII)I"); 1009 gInputStreamClassInfo.mSkipMethod = GetMethodIDOrDie(env, inputStreamClazz, "skip", "(J)J"); 1010 1011 jclass inputBufferClazz = FindClassOrDie(env, "java/nio/ByteBuffer"); 1012 gInputByteBufferClassInfo.mGetMethod = GetMethodIDOrDie(env, 1013 inputBufferClazz, "get", "([BII)Ljava/nio/ByteBuffer;"); 1014 } 1015 1016 static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr, 1017 jobject resultsPtr, jstring formattedCaptureTime) { 1018 ALOGV("%s:", __FUNCTION__); 1019 CameraMetadata characteristics; 1020 CameraMetadata results; 1021 if (CameraMetadata_getNativeMetadata(env, characteristicsPtr, &characteristics) != OK) { 1022 jniThrowException(env, "java/lang/AssertionError", 1023 "No native metadata defined for camera characteristics."); 1024 return; 1025 } 1026 if (CameraMetadata_getNativeMetadata(env, resultsPtr, &results) != OK) { 1027 jniThrowException(env, "java/lang/AssertionError", 1028 "No native metadata defined for capture results."); 1029 return; 1030 } 1031 1032 sp<NativeContext> nativeContext = new NativeContext(characteristics, results); 1033 1034 const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, nullptr); 1035 1036 size_t len = strlen(captureTime) + 1; 1037 if (len != NativeContext::DATETIME_COUNT) { 1038 jniThrowException(env, "java/lang/IllegalArgumentException", 1039 "Formatted capture time string length is not required 20 characters"); 1040 return; 1041 } 1042 1043 nativeContext->setCaptureTime(String8(captureTime)); 1044 1045 DngCreator_setNativeContext(env, thiz, nativeContext); 1046 } 1047 1048 static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t imageWidth, 1049 uint32_t imageHeight) { 1050 1051 NativeContext* nativeContext = DngCreator_getNativeContext(env, thiz); 1052 1053 if (nativeContext == nullptr) { 1054 jniThrowException(env, "java/lang/AssertionError", 1055 "No native context, must call init before other operations."); 1056 return nullptr; 1057 } 1058 1059 CameraMetadata characteristics = *(nativeContext->getCharacteristics()); 1060 CameraMetadata results = *(nativeContext->getResult()); 1061 1062 sp<TiffWriter> writer = new TiffWriter(); 1063 1064 uint32_t preWidth = 0; 1065 uint32_t preHeight = 0; 1066 { 1067 // Check dimensions 1068 camera_metadata_entry entry = 1069 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 1070 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer); 1071 preWidth = static_cast<uint32_t>(entry.data.i32[2]); 1072 preHeight = static_cast<uint32_t>(entry.data.i32[3]); 1073 1074 camera_metadata_entry pixelArrayEntry = 1075 characteristics.find(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE); 1076 uint32_t pixWidth = static_cast<uint32_t>(pixelArrayEntry.data.i32[0]); 1077 uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]); 1078 1079 if (!((imageWidth == preWidth && imageHeight == preHeight) || 1080 (imageWidth == pixWidth && imageHeight == pixHeight))) { 1081 jniThrowException(env, "java/lang/AssertionError", 1082 "Height and width of imate buffer did not match height and width of" 1083 "either the preCorrectionActiveArraySize or the pixelArraySize."); 1084 return nullptr; 1085 } 1086 } 1087 1088 1089 1090 writer->addIfd(TIFF_IFD_0); 1091 1092 status_t err = OK; 1093 1094 const uint32_t samplesPerPixel = 1; 1095 const uint32_t bitsPerSample = BITS_PER_SAMPLE; 1096 1097 OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB; 1098 uint8_t cfaPlaneColor[3] = {0, 1, 2}; 1099 uint8_t cfaEnum = -1; 1100 1101 // TODO: Greensplit. 1102 // TODO: Add remaining non-essential tags 1103 1104 // Setup main image tags 1105 1106 { 1107 // Set orientation 1108 uint16_t orientation = TAG_ORIENTATION_NORMAL; 1109 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), 1110 env, TAG_ORIENTATION, writer); 1111 } 1112 1113 { 1114 // Set subfiletype 1115 uint32_t subfileType = 0; // Main image 1116 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, 1117 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer); 1118 } 1119 1120 { 1121 // Set bits per sample 1122 uint16_t bits = static_cast<uint16_t>(bitsPerSample); 1123 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0), env, 1124 TAG_BITSPERSAMPLE, writer); 1125 } 1126 1127 { 1128 // Set compression 1129 uint16_t compression = 1; // None 1130 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression, 1131 TIFF_IFD_0), env, TAG_COMPRESSION, writer); 1132 } 1133 1134 { 1135 // Set dimensions 1136 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &imageWidth, TIFF_IFD_0), 1137 env, TAG_IMAGEWIDTH, writer); 1138 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &imageHeight, TIFF_IFD_0), 1139 env, TAG_IMAGELENGTH, writer); 1140 } 1141 1142 { 1143 // Set photometric interpretation 1144 uint16_t interpretation = 32803; // CFA 1145 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, 1146 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); 1147 } 1148 1149 { 1150 // Set blacklevel tags, using dynamic black level if available 1151 camera_metadata_entry entry = 1152 results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL); 1153 uint32_t blackLevelRational[8] = {0}; 1154 if (entry.count != 0) { 1155 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer); 1156 for (size_t i = 0; i < entry.count; i++) { 1157 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100); 1158 blackLevelRational[i * 2 + 1] = 100; 1159 } 1160 } else { 1161 // Fall back to static black level which is guaranteed 1162 entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN); 1163 BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer); 1164 for (size_t i = 0; i < entry.count; i++) { 1165 blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]); 1166 blackLevelRational[i * 2 + 1] = 1; 1167 } 1168 1169 } 1170 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational, 1171 TIFF_IFD_0), env, TAG_BLACKLEVEL, writer); 1172 1173 uint16_t repeatDim[2] = {2, 2}; 1174 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim, 1175 TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer); 1176 } 1177 1178 { 1179 // Set samples per pixel 1180 uint16_t samples = static_cast<uint16_t>(samplesPerPixel); 1181 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, TIFF_IFD_0), 1182 env, TAG_SAMPLESPERPIXEL, writer); 1183 } 1184 1185 { 1186 // Set planar configuration 1187 uint16_t config = 1; // Chunky 1188 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, 1189 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer); 1190 } 1191 1192 { 1193 // Set CFA pattern dimensions 1194 uint16_t repeatDim[2] = {2, 2}; 1195 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim, 1196 TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer); 1197 } 1198 1199 { 1200 // Set CFA pattern 1201 camera_metadata_entry entry = 1202 characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT); 1203 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer); 1204 1205 const int cfaLength = 4; 1206 cfaEnum = entry.data.u8[0]; 1207 uint8_t cfa[cfaLength]; 1208 if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) { 1209 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", 1210 "Invalid metadata for tag %d", TAG_CFAPATTERN); 1211 } 1212 1213 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), 1214 env, TAG_CFAPATTERN, writer); 1215 1216 opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum); 1217 } 1218 1219 { 1220 // Set CFA plane color 1221 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, 1222 TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer); 1223 } 1224 1225 { 1226 // Set CFA layout 1227 uint16_t cfaLayout = 1; 1228 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0), 1229 env, TAG_CFALAYOUT, writer); 1230 } 1231 1232 { 1233 // image description 1234 uint8_t imageDescription = '\0'; // empty 1235 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, 1236 TIFF_IFD_0), env, TAG_IMAGEDESCRIPTION, writer); 1237 } 1238 1239 { 1240 // make 1241 // Use "" to represent unknown make as suggested in TIFF/EP spec. 1242 std::string manufacturer = GetProperty("ro.product.manufacturer", ""); 1243 uint32_t count = static_cast<uint32_t>(manufacturer.size()) + 1; 1244 1245 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MAKE, count, 1246 reinterpret_cast<const uint8_t*>(manufacturer.c_str()), TIFF_IFD_0), env, TAG_MAKE, 1247 writer); 1248 } 1249 1250 { 1251 // model 1252 // Use "" to represent unknown model as suggested in TIFF/EP spec. 1253 std::string model = GetProperty("ro.product.model", ""); 1254 uint32_t count = static_cast<uint32_t>(model.size()) + 1; 1255 1256 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_MODEL, count, 1257 reinterpret_cast<const uint8_t*>(model.c_str()), TIFF_IFD_0), env, TAG_MODEL, 1258 writer); 1259 } 1260 1261 { 1262 // x resolution 1263 uint32_t xres[] = { 72, 1 }; // default 72 ppi 1264 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), 1265 env, TAG_XRESOLUTION, writer); 1266 1267 // y resolution 1268 uint32_t yres[] = { 72, 1 }; // default 72 ppi 1269 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), 1270 env, TAG_YRESOLUTION, writer); 1271 1272 uint16_t unit = 2; // inches 1273 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), 1274 env, TAG_RESOLUTIONUNIT, writer); 1275 } 1276 1277 { 1278 // software 1279 std::string software = GetProperty("ro.build.fingerprint", ""); 1280 uint32_t count = static_cast<uint32_t>(software.size()) + 1; 1281 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SOFTWARE, count, 1282 reinterpret_cast<const uint8_t*>(software.c_str()), TIFF_IFD_0), env, TAG_SOFTWARE, 1283 writer); 1284 } 1285 1286 if (nativeContext->hasCaptureTime()) { 1287 // datetime 1288 String8 captureTime = nativeContext->getCaptureTime(); 1289 1290 if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT, 1291 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) { 1292 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", 1293 "Invalid metadata for tag %x", TAG_DATETIME); 1294 return nullptr; 1295 } 1296 1297 // datetime original 1298 if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT, 1299 reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) { 1300 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", 1301 "Invalid metadata for tag %x", TAG_DATETIMEORIGINAL); 1302 return nullptr; 1303 } 1304 } 1305 1306 { 1307 // TIFF/EP standard id 1308 uint8_t standardId[] = { 1, 0, 0, 0 }; 1309 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId, 1310 TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID, writer); 1311 } 1312 1313 { 1314 // copyright 1315 uint8_t copyright = '\0'; // empty 1316 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COPYRIGHT, 1, ©right, 1317 TIFF_IFD_0), env, TAG_COPYRIGHT, writer); 1318 } 1319 1320 { 1321 // exposure time 1322 camera_metadata_entry entry = 1323 results.find(ANDROID_SENSOR_EXPOSURE_TIME); 1324 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_EXPOSURETIME, writer); 1325 1326 int64_t exposureTime = *(entry.data.i64); 1327 1328 if (exposureTime < 0) { 1329 // Should be unreachable 1330 jniThrowException(env, "java/lang/IllegalArgumentException", 1331 "Negative exposure time in metadata"); 1332 return nullptr; 1333 } 1334 1335 // Ensure exposure time doesn't overflow (for exposures > 4s) 1336 uint32_t denominator = 1000000000; 1337 while (exposureTime > UINT32_MAX) { 1338 exposureTime >>= 1; 1339 denominator >>= 1; 1340 if (denominator == 0) { 1341 // Should be unreachable 1342 jniThrowException(env, "java/lang/IllegalArgumentException", 1343 "Exposure time too long"); 1344 return nullptr; 1345 } 1346 } 1347 1348 uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator }; 1349 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_EXPOSURETIME, 1, exposure, 1350 TIFF_IFD_0), env, TAG_EXPOSURETIME, writer); 1351 1352 } 1353 1354 { 1355 // ISO speed ratings 1356 camera_metadata_entry entry = 1357 results.find(ANDROID_SENSOR_SENSITIVITY); 1358 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ISOSPEEDRATINGS, writer); 1359 1360 int32_t tempIso = *(entry.data.i32); 1361 if (tempIso < 0) { 1362 jniThrowException(env, "java/lang/IllegalArgumentException", 1363 "Negative ISO value"); 1364 return nullptr; 1365 } 1366 1367 if (tempIso > UINT16_MAX) { 1368 ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__); 1369 tempIso = UINT16_MAX; 1370 } 1371 1372 uint16_t iso = static_cast<uint16_t>(tempIso); 1373 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso, 1374 TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS, writer); 1375 } 1376 1377 { 1378 // Baseline exposure 1379 camera_metadata_entry entry = 1380 results.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST); 1381 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BASELINEEXPOSURE, writer); 1382 1383 // post RAW gain should be boostValue / 100 1384 double postRAWGain = static_cast<double> (entry.data.i32[0]) / 100.f; 1385 // Baseline exposure should be in EV units so log2(gain) = 1386 // log10(gain)/log10(2) 1387 double baselineExposure = std::log(postRAWGain) / std::log(2.0f); 1388 int32_t baseExposureSRat[] = { static_cast<int32_t> (baselineExposure * 100), 1389 100 }; 1390 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BASELINEEXPOSURE, 1, 1391 baseExposureSRat, TIFF_IFD_0), env, TAG_BASELINEEXPOSURE, writer); 1392 } 1393 1394 { 1395 // focal length 1396 camera_metadata_entry entry = 1397 results.find(ANDROID_LENS_FOCAL_LENGTH); 1398 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FOCALLENGTH, writer); 1399 1400 uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; 1401 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength, 1402 TIFF_IFD_0), env, TAG_FOCALLENGTH, writer); 1403 } 1404 1405 { 1406 // f number 1407 camera_metadata_entry entry = 1408 results.find(ANDROID_LENS_APERTURE); 1409 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_FNUMBER, writer); 1410 1411 uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 }; 1412 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FNUMBER, 1, fnum, 1413 TIFF_IFD_0), env, TAG_FNUMBER, writer); 1414 } 1415 1416 { 1417 // Set DNG version information 1418 uint8_t version[4] = {1, 4, 0, 0}; 1419 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0), 1420 env, TAG_DNGVERSION, writer); 1421 1422 uint8_t backwardVersion[4] = {1, 1, 0, 0}; 1423 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DNGBACKWARDVERSION, 4, backwardVersion, 1424 TIFF_IFD_0), env, TAG_DNGBACKWARDVERSION, writer); 1425 } 1426 1427 { 1428 // Set whitelevel 1429 camera_metadata_entry entry = 1430 characteristics.find(ANDROID_SENSOR_INFO_WHITE_LEVEL); 1431 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_WHITELEVEL, writer); 1432 uint32_t whiteLevel = static_cast<uint32_t>(entry.data.i32[0]); 1433 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_WHITELEVEL, 1, &whiteLevel, TIFF_IFD_0), 1434 env, TAG_WHITELEVEL, writer); 1435 } 1436 1437 { 1438 // Set default scale 1439 uint32_t defaultScale[4] = {1, 1, 1, 1}; 1440 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_DEFAULTSCALE, 2, defaultScale, 1441 TIFF_IFD_0), env, TAG_DEFAULTSCALE, writer); 1442 } 1443 1444 bool singleIlluminant = false; 1445 { 1446 // Set calibration illuminants 1447 camera_metadata_entry entry1 = 1448 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1); 1449 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CALIBRATIONILLUMINANT1, writer); 1450 camera_metadata_entry entry2 = 1451 characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT2); 1452 if (entry2.count == 0) { 1453 singleIlluminant = true; 1454 } 1455 uint16_t ref1 = entry1.data.u8[0]; 1456 1457 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT1, 1, &ref1, 1458 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT1, writer); 1459 1460 if (!singleIlluminant) { 1461 uint16_t ref2 = entry2.data.u8[0]; 1462 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CALIBRATIONILLUMINANT2, 1, &ref2, 1463 TIFF_IFD_0), env, TAG_CALIBRATIONILLUMINANT2, writer); 1464 } 1465 } 1466 1467 { 1468 // Set color transforms 1469 camera_metadata_entry entry1 = 1470 characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1); 1471 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_COLORMATRIX1, writer); 1472 1473 int32_t colorTransform1[entry1.count * 2]; 1474 1475 size_t ctr = 0; 1476 for(size_t i = 0; i < entry1.count; ++i) { 1477 colorTransform1[ctr++] = entry1.data.r[i].numerator; 1478 colorTransform1[ctr++] = entry1.data.r[i].denominator; 1479 } 1480 1481 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX1, entry1.count, 1482 colorTransform1, TIFF_IFD_0), env, TAG_COLORMATRIX1, writer); 1483 1484 if (!singleIlluminant) { 1485 camera_metadata_entry entry2 = characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM2); 1486 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_COLORMATRIX2, writer); 1487 int32_t colorTransform2[entry2.count * 2]; 1488 1489 ctr = 0; 1490 for(size_t i = 0; i < entry2.count; ++i) { 1491 colorTransform2[ctr++] = entry2.data.r[i].numerator; 1492 colorTransform2[ctr++] = entry2.data.r[i].denominator; 1493 } 1494 1495 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COLORMATRIX2, entry2.count, 1496 colorTransform2, TIFF_IFD_0), env, TAG_COLORMATRIX2, writer); 1497 } 1498 } 1499 1500 { 1501 // Set calibration transforms 1502 camera_metadata_entry entry1 = 1503 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1); 1504 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_CAMERACALIBRATION1, writer); 1505 1506 int32_t calibrationTransform1[entry1.count * 2]; 1507 1508 size_t ctr = 0; 1509 for(size_t i = 0; i < entry1.count; ++i) { 1510 calibrationTransform1[ctr++] = entry1.data.r[i].numerator; 1511 calibrationTransform1[ctr++] = entry1.data.r[i].denominator; 1512 } 1513 1514 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION1, entry1.count, 1515 calibrationTransform1, TIFF_IFD_0), env, TAG_CAMERACALIBRATION1, writer); 1516 1517 if (!singleIlluminant) { 1518 camera_metadata_entry entry2 = 1519 characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM2); 1520 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_CAMERACALIBRATION2, writer); 1521 int32_t calibrationTransform2[entry2.count * 2]; 1522 1523 ctr = 0; 1524 for(size_t i = 0; i < entry2.count; ++i) { 1525 calibrationTransform2[ctr++] = entry2.data.r[i].numerator; 1526 calibrationTransform2[ctr++] = entry2.data.r[i].denominator; 1527 } 1528 1529 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CAMERACALIBRATION2, entry2.count, 1530 calibrationTransform2, TIFF_IFD_0), env, TAG_CAMERACALIBRATION2, writer); 1531 } 1532 } 1533 1534 { 1535 // Set forward transforms 1536 camera_metadata_entry entry1 = 1537 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1); 1538 BAIL_IF_EMPTY_RET_NULL_SP(entry1, env, TAG_FORWARDMATRIX1, writer); 1539 1540 int32_t forwardTransform1[entry1.count * 2]; 1541 1542 size_t ctr = 0; 1543 for(size_t i = 0; i < entry1.count; ++i) { 1544 forwardTransform1[ctr++] = entry1.data.r[i].numerator; 1545 forwardTransform1[ctr++] = entry1.data.r[i].denominator; 1546 } 1547 1548 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX1, entry1.count, 1549 forwardTransform1, TIFF_IFD_0), env, TAG_FORWARDMATRIX1, writer); 1550 1551 if (!singleIlluminant) { 1552 camera_metadata_entry entry2 = 1553 characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX2); 1554 BAIL_IF_EMPTY_RET_NULL_SP(entry2, env, TAG_FORWARDMATRIX2, writer); 1555 int32_t forwardTransform2[entry2.count * 2]; 1556 1557 ctr = 0; 1558 for(size_t i = 0; i < entry2.count; ++i) { 1559 forwardTransform2[ctr++] = entry2.data.r[i].numerator; 1560 forwardTransform2[ctr++] = entry2.data.r[i].denominator; 1561 } 1562 1563 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FORWARDMATRIX2, entry2.count, 1564 forwardTransform2, TIFF_IFD_0), env, TAG_FORWARDMATRIX2, writer); 1565 } 1566 } 1567 1568 { 1569 // Set camera neutral 1570 camera_metadata_entry entry = 1571 results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT); 1572 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ASSHOTNEUTRAL, writer); 1573 uint32_t cameraNeutral[entry.count * 2]; 1574 1575 size_t ctr = 0; 1576 for(size_t i = 0; i < entry.count; ++i) { 1577 cameraNeutral[ctr++] = 1578 static_cast<uint32_t>(entry.data.r[i].numerator); 1579 cameraNeutral[ctr++] = 1580 static_cast<uint32_t>(entry.data.r[i].denominator); 1581 } 1582 1583 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ASSHOTNEUTRAL, entry.count, cameraNeutral, 1584 TIFF_IFD_0), env, TAG_ASSHOTNEUTRAL, writer); 1585 } 1586 1587 1588 { 1589 // Set dimensions 1590 if (calculateAndSetCrop(env, characteristics, writer) != OK) { 1591 return nullptr; 1592 } 1593 camera_metadata_entry entry = 1594 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 1595 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_ACTIVEAREA, writer); 1596 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]); 1597 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]); 1598 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); 1599 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); 1600 1601 // If we only have a buffer containing the pre-correction rectangle, ignore the offset 1602 // relative to the pixel array. 1603 if (imageWidth == width && imageHeight == height) { 1604 xmin = 0; 1605 ymin = 0; 1606 } 1607 1608 uint32_t activeArea[] = {ymin, xmin, ymin + height, xmin + width}; 1609 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ACTIVEAREA, 4, activeArea, TIFF_IFD_0), 1610 env, TAG_ACTIVEAREA, writer); 1611 } 1612 1613 { 1614 // Setup unique camera model tag 1615 std::string model = GetProperty("ro.product.model", ""); 1616 std::string manufacturer = GetProperty("ro.product.manufacturer", ""); 1617 std::string brand = GetProperty("ro.product.brand", ""); 1618 1619 String8 cameraModel(model.c_str()); 1620 cameraModel += "-"; 1621 cameraModel += manufacturer.c_str(); 1622 cameraModel += "-"; 1623 cameraModel += brand.c_str(); 1624 1625 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1, 1626 reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env, 1627 TAG_UNIQUECAMERAMODEL, writer); 1628 } 1629 1630 { 1631 // Setup sensor noise model 1632 camera_metadata_entry entry = 1633 results.find(ANDROID_SENSOR_NOISE_PROFILE); 1634 1635 const status_t numPlaneColors = 3; 1636 const status_t numCfaChannels = 4; 1637 1638 uint8_t cfaOut[numCfaChannels]; 1639 if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) { 1640 jniThrowException(env, "java/lang/IllegalArgumentException", 1641 "Invalid CFA from camera characteristics"); 1642 return nullptr; 1643 } 1644 1645 double noiseProfile[numPlaneColors * 2]; 1646 1647 if (entry.count > 0) { 1648 if (entry.count != numCfaChannels * 2) { 1649 ALOGW("%s: Invalid entry count %zu for noise profile returned " 1650 "in characteristics, no noise profile tag written...", 1651 __FUNCTION__, entry.count); 1652 } else { 1653 if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels, 1654 cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) { 1655 1656 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NOISEPROFILE, 1657 numPlaneColors * 2, noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, 1658 writer); 1659 } else { 1660 ALOGW("%s: Error converting coefficients for noise profile, no noise profile" 1661 " tag written...", __FUNCTION__); 1662 } 1663 } 1664 } else { 1665 ALOGW("%s: No noise profile found in result metadata. Image quality may be reduced.", 1666 __FUNCTION__); 1667 } 1668 } 1669 1670 { 1671 // Set up opcode List 2 1672 OpcodeListBuilder builder; 1673 status_t err = OK; 1674 1675 // Set up lens shading map 1676 camera_metadata_entry entry1 = 1677 characteristics.find(ANDROID_LENS_INFO_SHADING_MAP_SIZE); 1678 1679 uint32_t lsmWidth = 0; 1680 uint32_t lsmHeight = 0; 1681 1682 if (entry1.count != 0) { 1683 lsmWidth = static_cast<uint32_t>(entry1.data.i32[0]); 1684 lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]); 1685 } 1686 1687 camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP); 1688 1689 camera_metadata_entry entry = 1690 characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); 1691 BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_IMAGEWIDTH, writer); 1692 uint32_t xmin = static_cast<uint32_t>(entry.data.i32[0]); 1693 uint32_t ymin = static_cast<uint32_t>(entry.data.i32[1]); 1694 uint32_t width = static_cast<uint32_t>(entry.data.i32[2]); 1695 uint32_t height = static_cast<uint32_t>(entry.data.i32[3]); 1696 if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) { 1697 // GainMap rectangle is relative to the active area origin. 1698 err = builder.addGainMapsForMetadata(lsmWidth, 1699 lsmHeight, 1700 0, 1701 0, 1702 height, 1703 width, 1704 opcodeCfaLayout, 1705 entry2.data.f); 1706 if (err != OK) { 1707 ALOGE("%s: Could not add Lens shading map.", __FUNCTION__); 1708 jniThrowRuntimeException(env, "failed to add lens shading map."); 1709 return nullptr; 1710 } 1711 } 1712 1713 1714 // Set up bad pixel correction list 1715 camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP); 1716 1717 if ((entry3.count % 2) != 0) { 1718 ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!", 1719 __FUNCTION__); 1720 jniThrowRuntimeException(env, "failed to add hotpixel map."); 1721 return nullptr; 1722 } 1723 1724 // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag 1725 std::vector<uint32_t> v; 1726 for (size_t i = 0; i < entry3.count; i += 2) { 1727 int32_t x = entry3.data.i32[i]; 1728 int32_t y = entry3.data.i32[i + 1]; 1729 x -= static_cast<int32_t>(xmin); 1730 y -= static_cast<int32_t>(ymin); 1731 if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width || 1732 static_cast<uint32_t>(y) >= height) { 1733 continue; 1734 } 1735 v.push_back(x); 1736 v.push_back(y); 1737 } 1738 const uint32_t* badPixels = &v[0]; 1739 uint32_t badPixelCount = v.size(); 1740 1741 if (badPixelCount > 0) { 1742 err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout); 1743 1744 if (err != OK) { 1745 ALOGE("%s: Could not add hotpixel map.", __FUNCTION__); 1746 jniThrowRuntimeException(env, "failed to add hotpixel map."); 1747 return nullptr; 1748 } 1749 } 1750 1751 1752 size_t listSize = builder.getSize(); 1753 uint8_t opcodeListBuf[listSize]; 1754 err = builder.buildOpList(opcodeListBuf); 1755 if (err == OK) { 1756 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST2, listSize, opcodeListBuf, 1757 TIFF_IFD_0), env, TAG_OPCODELIST2, writer); 1758 } else { 1759 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading" 1760 "map.", __FUNCTION__); 1761 jniThrowRuntimeException(env, "failed to construct opcode list for distortion" 1762 " correction and lens shading map"); 1763 return nullptr; 1764 } 1765 } 1766 1767 { 1768 // Set up opcode List 3 1769 OpcodeListBuilder builder; 1770 status_t err = OK; 1771 1772 // Set up rectilinear distortion correction 1773 float distortion[6] {1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; 1774 bool gotDistortion = false; 1775 1776 camera_metadata_entry entry4 = 1777 results.find(ANDROID_LENS_INTRINSIC_CALIBRATION); 1778 1779 if (entry4.count == 5) { 1780 float cx = entry4.data.f[/*c_x*/2]; 1781 float cy = entry4.data.f[/*c_y*/3]; 1782 // Assuming f_x = f_y, or at least close enough. 1783 // Also assuming s = 0, or at least close enough. 1784 float f = entry4.data.f[/*f_x*/0]; 1785 1786 camera_metadata_entry entry3 = 1787 results.find(ANDROID_LENS_DISTORTION); 1788 if (entry3.count == 5) { 1789 gotDistortion = true; 1790 float m_x = std::fmaxf(preWidth-1 - cx, cx); 1791 float m_y = std::fmaxf(preHeight-1 - cy, cy); 1792 float m_sq = m_x*m_x + m_y*m_y; 1793 float m = sqrtf(m_sq); // distance to farthest corner from optical center 1794 float f_sq = f * f; 1795 // Conversion factors from Camera2 K factors for new LENS_DISTORTION field 1796 // to DNG spec. 1797 // 1798 // Camera2 / OpenCV assume distortion is applied in a space where focal length 1799 // is factored out, while DNG assumes a normalized space where the distance 1800 // from optical center to the farthest corner is 1. 1801 // Scale from camera2 to DNG spec accordingly. 1802 // distortion[0] is always 1 with the new LENS_DISTORTION field. 1803 const double convCoeff[5] = { 1804 m_sq / f_sq, 1805 pow(m_sq, 2) / pow(f_sq, 2), 1806 pow(m_sq, 3) / pow(f_sq, 3), 1807 m / f, 1808 m / f 1809 }; 1810 for (size_t i = 0; i < entry3.count; i++) { 1811 distortion[i+1] = convCoeff[i] * entry3.data.f[i]; 1812 } 1813 } else { 1814 entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION); 1815 if (entry3.count == 6) { 1816 gotDistortion = true; 1817 // Conversion factors from Camera2 K factors to DNG spec. K factors: 1818 // 1819 // Note: these are necessary because our unit system assumes a 1820 // normalized max radius of sqrt(2), whereas the DNG spec's 1821 // WarpRectilinear opcode assumes a normalized max radius of 1. 1822 // Thus, each K coefficient must include the domain scaling 1823 // factor (the DNG domain is scaled by sqrt(2) to emulate the 1824 // domain used by the Camera2 specification). 1825 const double convCoeff[6] = { 1826 sqrt(2), 1827 2 * sqrt(2), 1828 4 * sqrt(2), 1829 8 * sqrt(2), 1830 2, 1831 2 1832 }; 1833 for (size_t i = 0; i < entry3.count; i++) { 1834 distortion[i] = entry3.data.f[i] * convCoeff[i]; 1835 } 1836 } 1837 } 1838 if (gotDistortion) { 1839 err = builder.addWarpRectilinearForMetadata(distortion, preWidth, preHeight, cx, 1840 cy); 1841 if (err != OK) { 1842 ALOGE("%s: Could not add distortion correction.", __FUNCTION__); 1843 jniThrowRuntimeException(env, "failed to add distortion correction."); 1844 return nullptr; 1845 } 1846 } 1847 } 1848 1849 size_t listSize = builder.getSize(); 1850 uint8_t opcodeListBuf[listSize]; 1851 err = builder.buildOpList(opcodeListBuf); 1852 if (err == OK) { 1853 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_OPCODELIST3, listSize, opcodeListBuf, 1854 TIFF_IFD_0), env, TAG_OPCODELIST3, writer); 1855 } else { 1856 ALOGE("%s: Could not build list of opcodes for distortion correction and lens shading" 1857 "map.", __FUNCTION__); 1858 jniThrowRuntimeException(env, "failed to construct opcode list for distortion" 1859 " correction and lens shading map"); 1860 return nullptr; 1861 } 1862 } 1863 1864 { 1865 // Set up orientation tags. 1866 // Note: There's only one orientation field for the whole file, in IFD0 1867 // The main image and any thumbnails therefore have the same orientation. 1868 uint16_t orientation = nativeContext->getOrientation(); 1869 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0), 1870 env, TAG_ORIENTATION, writer); 1871 1872 } 1873 1874 if (nativeContext->hasDescription()){ 1875 // Set Description 1876 String8 description = nativeContext->getDescription(); 1877 size_t len = description.bytes() + 1; 1878 if (writer->addEntry(TAG_IMAGEDESCRIPTION, len, 1879 reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) { 1880 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", 1881 "Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION); 1882 } 1883 } 1884 1885 if (nativeContext->hasGpsData()) { 1886 // Set GPS tags 1887 GpsData gpsData = nativeContext->getGpsData(); 1888 if (!writer->hasIfd(TIFF_IFD_GPSINFO)) { 1889 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_GPSINFO, TiffWriter::GPSINFO) != OK) { 1890 ALOGE("%s: Failed to add GpsInfo IFD %u to IFD %u", __FUNCTION__, TIFF_IFD_GPSINFO, 1891 TIFF_IFD_0); 1892 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add GPSINFO"); 1893 return nullptr; 1894 } 1895 } 1896 1897 { 1898 uint8_t version[] = {2, 3, 0, 0}; 1899 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSVERSIONID, 4, version, 1900 TIFF_IFD_GPSINFO), env, TAG_GPSVERSIONID, writer); 1901 } 1902 1903 { 1904 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDEREF, 1905 GpsData::GPS_REF_LENGTH, gpsData.mLatitudeRef, TIFF_IFD_GPSINFO), env, 1906 TAG_GPSLATITUDEREF, writer); 1907 } 1908 1909 { 1910 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDEREF, 1911 GpsData::GPS_REF_LENGTH, gpsData.mLongitudeRef, TIFF_IFD_GPSINFO), env, 1912 TAG_GPSLONGITUDEREF, writer); 1913 } 1914 1915 { 1916 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLATITUDE, 3, gpsData.mLatitude, 1917 TIFF_IFD_GPSINFO), env, TAG_GPSLATITUDE, writer); 1918 } 1919 1920 { 1921 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSLONGITUDE, 3, gpsData.mLongitude, 1922 TIFF_IFD_GPSINFO), env, TAG_GPSLONGITUDE, writer); 1923 } 1924 1925 { 1926 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSTIMESTAMP, 3, gpsData.mTimestamp, 1927 TIFF_IFD_GPSINFO), env, TAG_GPSTIMESTAMP, writer); 1928 } 1929 1930 { 1931 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_GPSDATESTAMP, 1932 GpsData::GPS_DATE_LENGTH, gpsData.mDate, TIFF_IFD_GPSINFO), env, 1933 TAG_GPSDATESTAMP, writer); 1934 } 1935 } 1936 1937 1938 if (nativeContext->hasThumbnail()) { 1939 if (!writer->hasIfd(TIFF_IFD_SUB1)) { 1940 if (writer->addSubIfd(TIFF_IFD_0, TIFF_IFD_SUB1) != OK) { 1941 ALOGE("%s: Failed to add SubIFD %u to IFD %u", __FUNCTION__, TIFF_IFD_SUB1, 1942 TIFF_IFD_0); 1943 jniThrowException(env, "java/lang/IllegalStateException", "Failed to add SubIFD"); 1944 return nullptr; 1945 } 1946 } 1947 1948 Vector<uint16_t> tagsToMove; 1949 tagsToMove.add(TAG_NEWSUBFILETYPE); 1950 tagsToMove.add(TAG_ACTIVEAREA); 1951 tagsToMove.add(TAG_BITSPERSAMPLE); 1952 tagsToMove.add(TAG_COMPRESSION); 1953 tagsToMove.add(TAG_IMAGEWIDTH); 1954 tagsToMove.add(TAG_IMAGELENGTH); 1955 tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION); 1956 tagsToMove.add(TAG_BLACKLEVEL); 1957 tagsToMove.add(TAG_BLACKLEVELREPEATDIM); 1958 tagsToMove.add(TAG_SAMPLESPERPIXEL); 1959 tagsToMove.add(TAG_PLANARCONFIGURATION); 1960 tagsToMove.add(TAG_CFAREPEATPATTERNDIM); 1961 tagsToMove.add(TAG_CFAPATTERN); 1962 tagsToMove.add(TAG_CFAPLANECOLOR); 1963 tagsToMove.add(TAG_CFALAYOUT); 1964 tagsToMove.add(TAG_XRESOLUTION); 1965 tagsToMove.add(TAG_YRESOLUTION); 1966 tagsToMove.add(TAG_RESOLUTIONUNIT); 1967 tagsToMove.add(TAG_WHITELEVEL); 1968 tagsToMove.add(TAG_DEFAULTSCALE); 1969 tagsToMove.add(TAG_DEFAULTCROPORIGIN); 1970 tagsToMove.add(TAG_DEFAULTCROPSIZE); 1971 tagsToMove.add(TAG_OPCODELIST2); 1972 tagsToMove.add(TAG_OPCODELIST3); 1973 1974 if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) { 1975 jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries"); 1976 return nullptr; 1977 } 1978 1979 // Setup thumbnail tags 1980 1981 { 1982 // Set photometric interpretation 1983 uint16_t interpretation = 2; // RGB 1984 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1, 1985 &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer); 1986 } 1987 1988 { 1989 // Set planar configuration 1990 uint16_t config = 1; // Chunky 1991 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config, 1992 TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer); 1993 } 1994 1995 { 1996 // Set samples per pixel 1997 uint16_t samples = SAMPLES_PER_RGB_PIXEL; 1998 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples, 1999 TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer); 2000 } 2001 2002 { 2003 // Set bits per sample 2004 uint16_t bits[SAMPLES_PER_RGB_PIXEL]; 2005 for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE; 2006 BAIL_IF_INVALID_RET_NULL_SP( 2007 writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0), 2008 env, TAG_BITSPERSAMPLE, writer); 2009 } 2010 2011 { 2012 // Set subfiletype 2013 uint32_t subfileType = 1; // Thumbnail image 2014 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType, 2015 TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer); 2016 } 2017 2018 { 2019 // Set compression 2020 uint16_t compression = 1; // None 2021 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression, 2022 TIFF_IFD_0), env, TAG_COMPRESSION, writer); 2023 } 2024 2025 { 2026 // Set dimensions 2027 uint32_t uWidth = nativeContext->getThumbnailWidth(); 2028 uint32_t uHeight = nativeContext->getThumbnailHeight(); 2029 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0), 2030 env, TAG_IMAGEWIDTH, writer); 2031 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0), 2032 env, TAG_IMAGELENGTH, writer); 2033 } 2034 2035 { 2036 // x resolution 2037 uint32_t xres[] = { 72, 1 }; // default 72 ppi 2038 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0), 2039 env, TAG_XRESOLUTION, writer); 2040 2041 // y resolution 2042 uint32_t yres[] = { 72, 1 }; // default 72 ppi 2043 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0), 2044 env, TAG_YRESOLUTION, writer); 2045 2046 uint16_t unit = 2; // inches 2047 BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0), 2048 env, TAG_RESOLUTIONUNIT, writer); 2049 } 2050 } 2051 2052 if (writer->addStrip(TIFF_IFD_0) != OK) { 2053 ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__); 2054 jniThrowException(env, "java/lang/IllegalStateException", 2055 "Failed to setup thumbnail strip tags."); 2056 return nullptr; 2057 } 2058 2059 if (writer->hasIfd(TIFF_IFD_SUB1)) { 2060 if (writer->addStrip(TIFF_IFD_SUB1) != OK) { 2061 ALOGE("%s: Could not main image strip tags.", __FUNCTION__); 2062 jniThrowException(env, "java/lang/IllegalStateException", 2063 "Failed to setup main image strip tags."); 2064 return nullptr; 2065 } 2066 } 2067 return writer; 2068 } 2069 2070 static void DngCreator_destroy(JNIEnv* env, jobject thiz) { 2071 ALOGV("%s:", __FUNCTION__); 2072 DngCreator_setNativeContext(env, thiz, nullptr); 2073 } 2074 2075 static void DngCreator_nativeSetOrientation(JNIEnv* env, jobject thiz, jint orient) { 2076 ALOGV("%s:", __FUNCTION__); 2077 2078 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2079 if (context == nullptr) { 2080 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2081 jniThrowException(env, "java/lang/AssertionError", 2082 "setOrientation called with uninitialized DngCreator"); 2083 return; 2084 } 2085 2086 uint16_t orientation = static_cast<uint16_t>(orient); 2087 context->setOrientation(orientation); 2088 } 2089 2090 static void DngCreator_nativeSetDescription(JNIEnv* env, jobject thiz, jstring description) { 2091 ALOGV("%s:", __FUNCTION__); 2092 2093 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2094 if (context == nullptr) { 2095 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2096 jniThrowException(env, "java/lang/AssertionError", 2097 "setDescription called with uninitialized DngCreator"); 2098 return; 2099 } 2100 2101 const char* desc = env->GetStringUTFChars(description, nullptr); 2102 context->setDescription(String8(desc)); 2103 env->ReleaseStringUTFChars(description, desc); 2104 } 2105 2106 static void DngCreator_nativeSetGpsTags(JNIEnv* env, jobject thiz, jintArray latTag, 2107 jstring latRef, jintArray longTag, jstring longRef, jstring dateTag, jintArray timeTag) { 2108 ALOGV("%s:", __FUNCTION__); 2109 2110 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2111 if (context == nullptr) { 2112 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2113 jniThrowException(env, "java/lang/AssertionError", 2114 "setGpsTags called with uninitialized DngCreator"); 2115 return; 2116 } 2117 2118 GpsData data; 2119 2120 jsize latLen = env->GetArrayLength(latTag); 2121 jsize longLen = env->GetArrayLength(longTag); 2122 jsize timeLen = env->GetArrayLength(timeTag); 2123 if (latLen != GpsData::GPS_VALUE_LENGTH) { 2124 jniThrowException(env, "java/lang/IllegalArgumentException", 2125 "invalid latitude tag length"); 2126 return; 2127 } else if (longLen != GpsData::GPS_VALUE_LENGTH) { 2128 jniThrowException(env, "java/lang/IllegalArgumentException", 2129 "invalid longitude tag length"); 2130 return; 2131 } else if (timeLen != GpsData::GPS_VALUE_LENGTH) { 2132 jniThrowException(env, "java/lang/IllegalArgumentException", 2133 "invalid time tag length"); 2134 return; 2135 } 2136 2137 env->GetIntArrayRegion(latTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), 2138 reinterpret_cast<jint*>(&data.mLatitude)); 2139 env->GetIntArrayRegion(longTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), 2140 reinterpret_cast<jint*>(&data.mLongitude)); 2141 env->GetIntArrayRegion(timeTag, 0, static_cast<jsize>(GpsData::GPS_VALUE_LENGTH), 2142 reinterpret_cast<jint*>(&data.mTimestamp)); 2143 2144 2145 env->GetStringUTFRegion(latRef, 0, 1, reinterpret_cast<char*>(&data.mLatitudeRef)); 2146 data.mLatitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0'; 2147 env->GetStringUTFRegion(longRef, 0, 1, reinterpret_cast<char*>(&data.mLongitudeRef)); 2148 data.mLongitudeRef[GpsData::GPS_REF_LENGTH - 1] = '\0'; 2149 env->GetStringUTFRegion(dateTag, 0, GpsData::GPS_DATE_LENGTH - 1, 2150 reinterpret_cast<char*>(&data.mDate)); 2151 data.mDate[GpsData::GPS_DATE_LENGTH - 1] = '\0'; 2152 2153 context->setGpsData(data); 2154 } 2155 2156 static void DngCreator_nativeSetThumbnail(JNIEnv* env, jobject thiz, jobject buffer, jint width, 2157 jint height) { 2158 ALOGV("%s:", __FUNCTION__); 2159 2160 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2161 if (context == nullptr) { 2162 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2163 jniThrowException(env, "java/lang/AssertionError", 2164 "setThumbnail called with uninitialized DngCreator"); 2165 return; 2166 } 2167 2168 size_t fullSize = width * height * BYTES_PER_RGB_PIXEL; 2169 jlong capacity = env->GetDirectBufferCapacity(buffer); 2170 if (static_cast<uint64_t>(capacity) != static_cast<uint64_t>(fullSize)) { 2171 jniThrowExceptionFmt(env, "java/lang/AssertionError", 2172 "Invalid size %d for thumbnail, expected size was %d", 2173 capacity, fullSize); 2174 return; 2175 } 2176 2177 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); 2178 if (pixelBytes == nullptr) { 2179 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); 2180 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); 2181 return; 2182 } 2183 2184 if (!context->setThumbnail(pixelBytes, width, height)) { 2185 jniThrowException(env, "java/lang/IllegalStateException", 2186 "Failed to set thumbnail."); 2187 return; 2188 } 2189 } 2190 2191 // TODO: Refactor out common preamble for the two nativeWrite methods. 2192 static void DngCreator_nativeWriteImage(JNIEnv* env, jobject thiz, jobject outStream, jint width, 2193 jint height, jobject inBuffer, jint rowStride, jint pixStride, jlong offset, 2194 jboolean isDirect) { 2195 ALOGV("%s:", __FUNCTION__); 2196 ALOGV("%s: nativeWriteImage called with: width=%d, height=%d, " 2197 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width, 2198 height, rowStride, pixStride, offset); 2199 uint32_t rStride = static_cast<uint32_t>(rowStride); 2200 uint32_t pStride = static_cast<uint32_t>(pixStride); 2201 uint32_t uWidth = static_cast<uint32_t>(width); 2202 uint32_t uHeight = static_cast<uint32_t>(height); 2203 uint64_t uOffset = static_cast<uint64_t>(offset); 2204 2205 sp<JniOutputStream> out = new JniOutputStream(env, outStream); 2206 if(env->ExceptionCheck()) { 2207 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__); 2208 return; 2209 } 2210 2211 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2212 if (context == nullptr) { 2213 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2214 jniThrowException(env, "java/lang/AssertionError", 2215 "Write called with uninitialized DngCreator"); 2216 return; 2217 } 2218 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight); 2219 2220 if (writer.get() == nullptr) { 2221 return; 2222 } 2223 2224 // Validate DNG size 2225 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) { 2226 return; 2227 } 2228 2229 sp<JniInputByteBuffer> inBuf; 2230 Vector<StripSource*> sources; 2231 sp<DirectStripSource> thumbnailSource; 2232 uint32_t targetIfd = TIFF_IFD_0; 2233 2234 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1); 2235 2236 if (hasThumbnail) { 2237 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__); 2238 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE; 2239 uint32_t thumbWidth = context->getThumbnailWidth(); 2240 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0, 2241 thumbWidth, context->getThumbnailHeight(), bytesPerPixel, 2242 bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE, 2243 SAMPLES_PER_RGB_PIXEL); 2244 sources.add(thumbnailSource.get()); 2245 targetIfd = TIFF_IFD_SUB1; 2246 } 2247 2248 if (isDirect) { 2249 size_t fullSize = rStride * uHeight; 2250 jlong capacity = env->GetDirectBufferCapacity(inBuffer); 2251 if (capacity < 0 || fullSize + uOffset > static_cast<uint64_t>(capacity)) { 2252 jniThrowExceptionFmt(env, "java/lang/IllegalStateException", 2253 "Invalid size %d for Image, size given in metadata is %d at current stride", 2254 capacity, fullSize); 2255 return; 2256 } 2257 2258 uint8_t* pixelBytes = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(inBuffer)); 2259 if (pixelBytes == nullptr) { 2260 ALOGE("%s: Could not get native ByteBuffer", __FUNCTION__); 2261 jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid ByteBuffer"); 2262 return; 2263 } 2264 2265 ALOGV("%s: Using direct-type strip source.", __FUNCTION__); 2266 DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride, 2267 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 2268 sources.add(&stripSource); 2269 2270 status_t ret = OK; 2271 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 2272 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 2273 if (!env->ExceptionCheck()) { 2274 jniThrowExceptionFmt(env, "java/io/IOException", 2275 "Encountered error %d while writing file.", ret); 2276 } 2277 return; 2278 } 2279 } else { 2280 inBuf = new JniInputByteBuffer(env, inBuffer); 2281 2282 ALOGV("%s: Using input-type strip source.", __FUNCTION__); 2283 InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride, 2284 rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 2285 sources.add(&stripSource); 2286 2287 status_t ret = OK; 2288 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 2289 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 2290 if (!env->ExceptionCheck()) { 2291 jniThrowExceptionFmt(env, "java/io/IOException", 2292 "Encountered error %d while writing file.", ret); 2293 } 2294 return; 2295 } 2296 } 2297 } 2298 2299 static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream, 2300 jobject inStream, jint width, jint height, jlong offset) { 2301 ALOGV("%s:", __FUNCTION__); 2302 2303 uint32_t rowStride = width * BYTES_PER_SAMPLE; 2304 uint32_t pixStride = BYTES_PER_SAMPLE; 2305 uint32_t uWidth = static_cast<uint32_t>(width); 2306 uint32_t uHeight = static_cast<uint32_t>(height); 2307 uint64_t uOffset = static_cast<uint32_t>(offset); 2308 2309 ALOGV("%s: nativeWriteInputStream called with: width=%d, height=%d, " 2310 "rowStride=%d, pixStride=%d, offset=%" PRId64, __FUNCTION__, width, 2311 height, rowStride, pixStride, offset); 2312 2313 sp<JniOutputStream> out = new JniOutputStream(env, outStream); 2314 if (env->ExceptionCheck()) { 2315 ALOGE("%s: Could not allocate buffers for output stream", __FUNCTION__); 2316 return; 2317 } 2318 2319 NativeContext* context = DngCreator_getNativeContext(env, thiz); 2320 if (context == nullptr) { 2321 ALOGE("%s: Failed to initialize DngCreator", __FUNCTION__); 2322 jniThrowException(env, "java/lang/AssertionError", 2323 "Write called with uninitialized DngCreator"); 2324 return; 2325 } 2326 sp<TiffWriter> writer = DngCreator_setup(env, thiz, uWidth, uHeight); 2327 2328 if (writer.get() == nullptr) { 2329 return; 2330 } 2331 2332 // Validate DNG size 2333 if (!validateDngHeader(env, writer, *(context->getCharacteristics()), width, height)) { 2334 return; 2335 } 2336 2337 sp<DirectStripSource> thumbnailSource; 2338 uint32_t targetIfd = TIFF_IFD_0; 2339 bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1); 2340 Vector<StripSource*> sources; 2341 2342 if (hasThumbnail) { 2343 ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__); 2344 uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE; 2345 uint32_t width = context->getThumbnailWidth(); 2346 thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0, 2347 width, context->getThumbnailHeight(), bytesPerPixel, 2348 bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE, 2349 SAMPLES_PER_RGB_PIXEL); 2350 sources.add(thumbnailSource.get()); 2351 targetIfd = TIFF_IFD_SUB1; 2352 } 2353 2354 sp<JniInputStream> in = new JniInputStream(env, inStream); 2355 2356 ALOGV("%s: Using input-type strip source.", __FUNCTION__); 2357 InputStripSource stripSource(env, *in, targetIfd, uWidth, uHeight, pixStride, 2358 rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL); 2359 sources.add(&stripSource); 2360 2361 status_t ret = OK; 2362 if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) { 2363 ALOGE("%s: write failed with error %d.", __FUNCTION__, ret); 2364 if (!env->ExceptionCheck()) { 2365 jniThrowExceptionFmt(env, "java/io/IOException", 2366 "Encountered error %d while writing file.", ret); 2367 } 2368 return; 2369 } 2370 } 2371 2372 } /*extern "C" */ 2373 2374 static const JNINativeMethod gDngCreatorMethods[] = { 2375 {"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit}, 2376 {"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;" 2377 "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V", 2378 (void*) DngCreator_init}, 2379 {"nativeDestroy", "()V", (void*) DngCreator_destroy}, 2380 {"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation}, 2381 {"nativeSetDescription", "(Ljava/lang/String;)V", (void*) DngCreator_nativeSetDescription}, 2382 {"nativeSetGpsTags", "([ILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;[I)V", 2383 (void*) DngCreator_nativeSetGpsTags}, 2384 {"nativeSetThumbnail","(Ljava/nio/ByteBuffer;II)V", (void*) DngCreator_nativeSetThumbnail}, 2385 {"nativeWriteImage", "(Ljava/io/OutputStream;IILjava/nio/ByteBuffer;IIJZ)V", 2386 (void*) DngCreator_nativeWriteImage}, 2387 {"nativeWriteInputStream", "(Ljava/io/OutputStream;Ljava/io/InputStream;IIJ)V", 2388 (void*) DngCreator_nativeWriteInputStream}, 2389 }; 2390 2391 int register_android_hardware_camera2_DngCreator(JNIEnv *env) { 2392 return RegisterMethodsOrDie(env, 2393 "android/hardware/camera2/DngCreator", gDngCreatorMethods, NELEM(gDngCreatorMethods)); 2394 } 2395