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