DNG: Update TiffWriter to handle thumbnails and SubIfds.

- Fix SubIfd handling.
- Add StripSources, convenience functions for writing image strips.
- Update Input classes to use with JNI.
- Add skip method.
- Add tag definitions for GPS tags.
- Add name string to tag definitions.

Bug: 15112503

Change-Id: I9535b21261027f6c06a041c1621de8f865a0ad32
diff --git a/media/img_utils/src/TiffIfd.cpp b/media/img_utils/src/TiffIfd.cpp
index 1b3b40d..3fb00cc 100644
--- a/media/img_utils/src/TiffIfd.cpp
+++ b/media/img_utils/src/TiffIfd.cpp
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
-#include <img_utils/TiffIfd.h>
+#define LOG_TAG "TiffIfd"
+
+#include <img_utils/TagDefinitions.h>
 #include <img_utils/TiffHelpers.h>
+#include <img_utils/TiffIfd.h>
+#include <img_utils/TiffWriter.h>
 
 #include <utils/Log.h>
 
@@ -23,7 +27,7 @@
 namespace img_utils {
 
 TiffIfd::TiffIfd(uint32_t ifdId)
-        : mNextIfd(), mIfdId(ifdId) {}
+        : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
 
 TiffIfd::~TiffIfd() {}
 
@@ -52,6 +56,14 @@
     return mEntries[index];
 }
 
+void TiffIfd::removeEntry(uint16_t tag) {
+    ssize_t index = mEntries.indexOfTag(tag);
+    if (index >= 0) {
+        mEntries.removeAt(index);
+    }
+}
+
+
 void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
     mNextIfd = ifd;
 }
@@ -156,6 +168,198 @@
     return mIfdId;
 }
 
+status_t TiffIfd::validateAndSetStripTags() {
+    sp<TiffEntry> widthEntry = getEntry(TAG_IMAGEWIDTH);
+    if (widthEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a ImageWidth tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> heightEntry = getEntry(TAG_IMAGELENGTH);
+    if (heightEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a ImageLength tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> samplesEntry = getEntry(TAG_SAMPLESPERPIXEL);
+    if (samplesEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a SamplesPerPixel tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> bitsEntry = getEntry(TAG_BITSPERSAMPLE);
+    if (bitsEntry == NULL) {
+        ALOGE("%s: IFD %u doesn't have a BitsPerSample tag set", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t width = *(widthEntry->getData<uint32_t>());
+    uint32_t height = *(heightEntry->getData<uint32_t>());
+    uint16_t bitsPerSample = *(bitsEntry->getData<uint16_t>());
+    uint16_t samplesPerPixel = *(samplesEntry->getData<uint16_t>());
+
+    if ((bitsPerSample % 8) != 0) {
+        ALOGE("%s: BitsPerSample %d in IFD %u is not byte-aligned.", __FUNCTION__,
+                bitsPerSample, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t bytesPerSample = bitsPerSample / 8;
+
+    // Choose strip size as close to 8kb as possible without splitting rows.
+    // If the row length is >8kb, each strip will only contain a single row.
+    const uint32_t rowLengthBytes = bytesPerSample * samplesPerPixel * width;
+    const uint32_t idealChunkSize = (1 << 13); // 8kb
+    uint32_t rowsPerChunk = idealChunkSize / rowLengthBytes;
+    rowsPerChunk = (rowsPerChunk == 0) ? 1 : rowsPerChunk;
+    const uint32_t actualChunkSize = rowLengthBytes * rowsPerChunk;
+
+    const uint32_t lastChunkRows = height % rowsPerChunk;
+    const uint32_t lastChunkSize = lastChunkRows * rowLengthBytes;
+
+    if (actualChunkSize > /*max strip size for TIFF/EP*/65536) {
+        ALOGE("%s: Strip length too long.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    size_t numStrips = height / rowsPerChunk;
+
+    // Add another strip for the incomplete chunk.
+    if (lastChunkRows > 0) {
+        numStrips += 1;
+    }
+
+    // Put each row in it's own strip
+    uint32_t rowsPerStripVal = rowsPerChunk;
+    sp<TiffEntry> rowsPerStrip = TiffWriter::uncheckedBuildEntry(TAG_ROWSPERSTRIP, LONG, 1,
+            UNDEFINED_ENDIAN, &rowsPerStripVal);
+
+    if (rowsPerStrip == NULL) {
+        ALOGE("%s: Could not build entry for RowsPerStrip tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Vector<uint32_t> byteCounts;
+
+    for (size_t i = 0; i < numStrips; ++i) {
+        if (lastChunkRows > 0 && i == (numStrips - 1)) {
+            byteCounts.add(lastChunkSize);
+        } else {
+            byteCounts.add(actualChunkSize);
+        }
+    }
+
+    // Set byte counts for each strip
+    sp<TiffEntry> stripByteCounts = TiffWriter::uncheckedBuildEntry(TAG_STRIPBYTECOUNTS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, byteCounts.array());
+
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: Could not build entry for StripByteCounts tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    Vector<uint32_t> stripOffsetsVector;
+    stripOffsetsVector.resize(numStrips);
+
+    // Set uninitialized offsets
+    sp<TiffEntry> stripOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsetsVector.array());
+
+    if (stripOffsets == NULL) {
+        ALOGE("%s: Could not build entry for StripOffsets tag.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(stripByteCounts) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(rowsPerStrip) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if(addEntry(stripOffsets) != OK) {
+        ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    mStripOffsetsInitialized = true;
+    return OK;
+}
+
+bool TiffIfd::uninitializedOffsets() const {
+    return mStripOffsetsInitialized;
+}
+
+status_t TiffIfd::setStripOffset(uint32_t offset) {
+
+    // Get old offsets and bytecounts
+    sp<TiffEntry> oldOffsets = getEntry(TAG_STRIPOFFSETS);
+    if (oldOffsets == NULL) {
+        ALOGE("%s: IFD %u does not contain StripOffsets entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t offsetsCount = oldOffsets->getCount();
+    uint32_t byteCount = stripByteCounts->getCount();
+    if (offsetsCount != byteCount) {
+        ALOGE("%s: StripOffsets count (%u) doesn't match StripByteCounts count (%u) in IFD %u",
+            __FUNCTION__, offsetsCount, byteCount, mIfdId);
+        return BAD_VALUE;
+    }
+
+    const uint32_t* stripByteCountsArray = stripByteCounts->getData<uint32_t>();
+
+    size_t numStrips = offsetsCount;
+
+    Vector<uint32_t> stripOffsets;
+
+    // Calculate updated byte offsets
+    for (size_t i = 0; i < numStrips; ++i) {
+        stripOffsets.add(offset);
+        offset += stripByteCountsArray[i];
+    }
+
+    sp<TiffEntry> newOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG,
+            static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsets.array());
+
+    if (newOffsets == NULL) {
+        ALOGE("%s: Coult not build updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    if (addEntry(newOffsets) != OK) {
+        ALOGE("%s: Failed to add updated offsets entry in IFD %u", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+uint32_t TiffIfd::getStripSize() const {
+    sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS);
+    if (stripByteCounts == NULL) {
+        ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId);
+        return BAD_VALUE;
+    }
+
+    uint32_t count = stripByteCounts->getCount();
+    const uint32_t* byteCounts = stripByteCounts->getData<uint32_t>();
+
+    uint32_t total = 0;
+    for (size_t i = 0; i < static_cast<size_t>(count); ++i) {
+        total += byteCounts[i];
+    }
+    return total;
+}
+
 String8 TiffIfd::toString() const {
     size_t s = mEntries.size();
     String8 output;