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