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