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