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