diff options
Diffstat (limited to 'media/img_utils/src/TiffIfd.cpp')
-rw-r--r-- | media/img_utils/src/TiffIfd.cpp | 208 |
1 files changed, 206 insertions, 2 deletions
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 android { namespace img_utils { TiffIfd::TiffIfd(uint32_t ifdId) - : mNextIfd(), mIfdId(ifdId) {} + : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {} TiffIfd::~TiffIfd() {} @@ -52,6 +56,14 @@ sp<TiffEntry> TiffIfd::getEntry(uint16_t tag) const { 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 @@ uint32_t TiffIfd::getComparableValue() const { 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; |