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