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_TAG "TiffIfd" 18 19#include <img_utils/TagDefinitions.h> 20#include <img_utils/TiffHelpers.h> 21#include <img_utils/TiffIfd.h> 22#include <img_utils/TiffWriter.h> 23 24#include <utils/Log.h> 25 26namespace android { 27namespace img_utils { 28 29TiffIfd::TiffIfd(uint32_t ifdId) 30 : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {} 31 32TiffIfd::~TiffIfd() {} 33 34status_t TiffIfd::addEntry(const sp<TiffEntry>& entry) { 35 size_t size = mEntries.size(); 36 if (size >= MAX_IFD_ENTRIES) { 37 ALOGW("%s: Failed to add entry for tag 0x%x to IFD %u, too many entries in IFD!", 38 __FUNCTION__, entry->getTag(), mIfdId); 39 return BAD_INDEX; 40 } 41 42 if (mEntries.add(entry) < 0) { 43 ALOGW("%s: Failed to add entry for tag 0x%x to ifd %u.", __FUNCTION__, entry->getTag(), 44 mIfdId); 45 return BAD_INDEX; 46 } 47 return OK; 48} 49 50sp<TiffEntry> TiffIfd::getEntry(uint16_t tag) const { 51 ssize_t index = mEntries.indexOfTag(tag); 52 if (index < 0) { 53 ALOGW("%s: No entry for tag 0x%x in ifd %u.", __FUNCTION__, tag, mIfdId); 54 return NULL; 55 } 56 return mEntries[index]; 57} 58 59void TiffIfd::removeEntry(uint16_t tag) { 60 ssize_t index = mEntries.indexOfTag(tag); 61 if (index >= 0) { 62 mEntries.removeAt(index); 63 } 64} 65 66 67void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) { 68 mNextIfd = ifd; 69} 70 71sp<TiffIfd> TiffIfd::getNextIfd() const { 72 return mNextIfd; 73} 74 75uint32_t TiffIfd::checkAndGetOffset(uint32_t offset) const { 76 size_t size = mEntries.size(); 77 78 if (size > MAX_IFD_ENTRIES) { 79 ALOGW("%s: Could not calculate IFD offsets, IFD %u contains too many entries.", 80 __FUNCTION__, mIfdId); 81 return BAD_OFFSET; 82 } 83 84 if (size <= 0) { 85 ALOGW("%s: Could not calculate IFD offsets, IFD %u contains no entries.", __FUNCTION__, 86 mIfdId); 87 return BAD_OFFSET; 88 } 89 90 if (offset == BAD_OFFSET) { 91 ALOGW("%s: Could not calculate IFD offsets, IFD %u had a bad initial offset.", 92 __FUNCTION__, mIfdId); 93 return BAD_OFFSET; 94 } 95 96 uint32_t ifdSize = calculateIfdSize(size); 97 WORD_ALIGN(ifdSize); 98 return offset + ifdSize; 99} 100 101status_t TiffIfd::writeData(uint32_t offset, /*out*/EndianOutput* out) const { 102 assert((offset % TIFF_WORD_SIZE) == 0); 103 status_t ret = OK; 104 105 ALOGV("%s: IFD %u written to offset %u", __FUNCTION__, mIfdId, offset ); 106 uint32_t valueOffset = checkAndGetOffset(offset); 107 if (valueOffset == 0) { 108 return BAD_VALUE; 109 } 110 111 size_t size = mEntries.size(); 112 113 // Writer IFD header (2 bytes, number of entries). 114 uint16_t header = static_cast<uint16_t>(size); 115 BAIL_ON_FAIL(out->write(&header, 0, 1), ret); 116 117 // Write tag entries 118 for (size_t i = 0; i < size; ++i) { 119 BAIL_ON_FAIL(mEntries[i]->writeTagInfo(valueOffset, out), ret); 120 valueOffset += mEntries[i]->getSize(); 121 } 122 123 // Writer IFD footer (4 bytes, offset to next IFD). 124 uint32_t footer = (mNextIfd != NULL) ? offset + getSize() : 0; 125 BAIL_ON_FAIL(out->write(&footer, 0, 1), ret); 126 127 assert(out->getCurrentOffset() == offset + calculateIfdSize(size)); 128 129 // Write zeroes till word aligned 130 ZERO_TILL_WORD(out, calculateIfdSize(size), ret); 131 132 // Write values for each tag entry 133 for (size_t i = 0; i < size; ++i) { 134 size_t last = out->getCurrentOffset(); 135 // Only write values that are too large to fit in the 12-byte TIFF entry 136 if (mEntries[i]->getSize() > OFFSET_SIZE) { 137 BAIL_ON_FAIL(mEntries[i]->writeData(out->getCurrentOffset(), out), ret); 138 } 139 size_t next = out->getCurrentOffset(); 140 size_t diff = (next - last); 141 size_t actual = mEntries[i]->getSize(); 142 if (diff != actual) { 143 ALOGW("Sizes do not match for tag %x. Expected %zu, received %zu", 144 mEntries[i]->getTag(), actual, diff); 145 } 146 } 147 148 assert(out->getCurrentOffset() == offset + getSize()); 149 150 return ret; 151} 152 153size_t TiffIfd::getSize() const { 154 size_t size = mEntries.size(); 155 uint32_t total = calculateIfdSize(size); 156 WORD_ALIGN(total); 157 for (size_t i = 0; i < size; ++i) { 158 total += mEntries[i]->getSize(); 159 } 160 return total; 161} 162 163uint32_t TiffIfd::getId() const { 164 return mIfdId; 165} 166 167uint32_t TiffIfd::getComparableValue() const { 168 return mIfdId; 169} 170 171status_t TiffIfd::validateAndSetStripTags() { 172 sp<TiffEntry> widthEntry = getEntry(TAG_IMAGEWIDTH); 173 if (widthEntry == NULL) { 174 ALOGE("%s: IFD %u doesn't have a ImageWidth tag set", __FUNCTION__, mIfdId); 175 return BAD_VALUE; 176 } 177 178 sp<TiffEntry> heightEntry = getEntry(TAG_IMAGELENGTH); 179 if (heightEntry == NULL) { 180 ALOGE("%s: IFD %u doesn't have a ImageLength tag set", __FUNCTION__, mIfdId); 181 return BAD_VALUE; 182 } 183 184 sp<TiffEntry> samplesEntry = getEntry(TAG_SAMPLESPERPIXEL); 185 if (samplesEntry == NULL) { 186 ALOGE("%s: IFD %u doesn't have a SamplesPerPixel tag set", __FUNCTION__, mIfdId); 187 return BAD_VALUE; 188 } 189 190 sp<TiffEntry> bitsEntry = getEntry(TAG_BITSPERSAMPLE); 191 if (bitsEntry == NULL) { 192 ALOGE("%s: IFD %u doesn't have a BitsPerSample tag set", __FUNCTION__, mIfdId); 193 return BAD_VALUE; 194 } 195 196 uint32_t width = *(widthEntry->getData<uint32_t>()); 197 uint32_t height = *(heightEntry->getData<uint32_t>()); 198 uint16_t bitsPerSample = *(bitsEntry->getData<uint16_t>()); 199 uint16_t samplesPerPixel = *(samplesEntry->getData<uint16_t>()); 200 201 if ((bitsPerSample % 8) != 0) { 202 ALOGE("%s: BitsPerSample %d in IFD %u is not byte-aligned.", __FUNCTION__, 203 bitsPerSample, mIfdId); 204 return BAD_VALUE; 205 } 206 207 uint32_t bytesPerSample = bitsPerSample / 8; 208 209 // Choose strip size as close to 8kb as possible without splitting rows. 210 // If the row length is >8kb, each strip will only contain a single row. 211 const uint32_t rowLengthBytes = bytesPerSample * samplesPerPixel * width; 212 const uint32_t idealChunkSize = (1 << 13); // 8kb 213 uint32_t rowsPerChunk = idealChunkSize / rowLengthBytes; 214 rowsPerChunk = (rowsPerChunk == 0) ? 1 : rowsPerChunk; 215 const uint32_t actualChunkSize = rowLengthBytes * rowsPerChunk; 216 217 const uint32_t lastChunkRows = height % rowsPerChunk; 218 const uint32_t lastChunkSize = lastChunkRows * rowLengthBytes; 219 220 if (actualChunkSize > /*max strip size for TIFF/EP*/65536) { 221 ALOGE("%s: Strip length too long.", __FUNCTION__); 222 return BAD_VALUE; 223 } 224 225 size_t numStrips = height / rowsPerChunk; 226 227 // Add another strip for the incomplete chunk. 228 if (lastChunkRows > 0) { 229 numStrips += 1; 230 } 231 232 // Put each row in it's own strip 233 uint32_t rowsPerStripVal = rowsPerChunk; 234 sp<TiffEntry> rowsPerStrip = TiffWriter::uncheckedBuildEntry(TAG_ROWSPERSTRIP, LONG, 1, 235 UNDEFINED_ENDIAN, &rowsPerStripVal); 236 237 if (rowsPerStrip == NULL) { 238 ALOGE("%s: Could not build entry for RowsPerStrip tag.", __FUNCTION__); 239 return BAD_VALUE; 240 } 241 242 Vector<uint32_t> byteCounts; 243 244 for (size_t i = 0; i < numStrips; ++i) { 245 if (lastChunkRows > 0 && i == (numStrips - 1)) { 246 byteCounts.add(lastChunkSize); 247 } else { 248 byteCounts.add(actualChunkSize); 249 } 250 } 251 252 // Set byte counts for each strip 253 sp<TiffEntry> stripByteCounts = TiffWriter::uncheckedBuildEntry(TAG_STRIPBYTECOUNTS, LONG, 254 static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, byteCounts.array()); 255 256 if (stripByteCounts == NULL) { 257 ALOGE("%s: Could not build entry for StripByteCounts tag.", __FUNCTION__); 258 return BAD_VALUE; 259 } 260 261 Vector<uint32_t> stripOffsetsVector; 262 stripOffsetsVector.resize(numStrips); 263 264 // Set uninitialized offsets 265 sp<TiffEntry> stripOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG, 266 static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsetsVector.array()); 267 268 if (stripOffsets == NULL) { 269 ALOGE("%s: Could not build entry for StripOffsets tag.", __FUNCTION__); 270 return BAD_VALUE; 271 } 272 273 if(addEntry(stripByteCounts) != OK) { 274 ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId); 275 return BAD_VALUE; 276 } 277 278 if(addEntry(rowsPerStrip) != OK) { 279 ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId); 280 return BAD_VALUE; 281 } 282 283 if(addEntry(stripOffsets) != OK) { 284 ALOGE("%s: Could not add entry for StripByteCounts to IFD %u", __FUNCTION__, mIfdId); 285 return BAD_VALUE; 286 } 287 288 mStripOffsetsInitialized = true; 289 return OK; 290} 291 292bool TiffIfd::uninitializedOffsets() const { 293 return mStripOffsetsInitialized; 294} 295 296status_t TiffIfd::setStripOffset(uint32_t offset) { 297 298 // Get old offsets and bytecounts 299 sp<TiffEntry> oldOffsets = getEntry(TAG_STRIPOFFSETS); 300 if (oldOffsets == NULL) { 301 ALOGE("%s: IFD %u does not contain StripOffsets entry.", __FUNCTION__, mIfdId); 302 return BAD_VALUE; 303 } 304 305 sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS); 306 if (stripByteCounts == NULL) { 307 ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId); 308 return BAD_VALUE; 309 } 310 311 uint32_t offsetsCount = oldOffsets->getCount(); 312 uint32_t byteCount = stripByteCounts->getCount(); 313 if (offsetsCount != byteCount) { 314 ALOGE("%s: StripOffsets count (%u) doesn't match StripByteCounts count (%u) in IFD %u", 315 __FUNCTION__, offsetsCount, byteCount, mIfdId); 316 return BAD_VALUE; 317 } 318 319 const uint32_t* stripByteCountsArray = stripByteCounts->getData<uint32_t>(); 320 321 size_t numStrips = offsetsCount; 322 323 Vector<uint32_t> stripOffsets; 324 325 // Calculate updated byte offsets 326 for (size_t i = 0; i < numStrips; ++i) { 327 stripOffsets.add(offset); 328 offset += stripByteCountsArray[i]; 329 } 330 331 sp<TiffEntry> newOffsets = TiffWriter::uncheckedBuildEntry(TAG_STRIPOFFSETS, LONG, 332 static_cast<uint32_t>(numStrips), UNDEFINED_ENDIAN, stripOffsets.array()); 333 334 if (newOffsets == NULL) { 335 ALOGE("%s: Coult not build updated offsets entry in IFD %u", __FUNCTION__, mIfdId); 336 return BAD_VALUE; 337 } 338 339 if (addEntry(newOffsets) != OK) { 340 ALOGE("%s: Failed to add updated offsets entry in IFD %u", __FUNCTION__, mIfdId); 341 return BAD_VALUE; 342 } 343 return OK; 344} 345 346uint32_t TiffIfd::getStripSize() const { 347 sp<TiffEntry> stripByteCounts = getEntry(TAG_STRIPBYTECOUNTS); 348 if (stripByteCounts == NULL) { 349 ALOGE("%s: IFD %u does not contain StripByteCounts entry.", __FUNCTION__, mIfdId); 350 return BAD_VALUE; 351 } 352 353 uint32_t count = stripByteCounts->getCount(); 354 const uint32_t* byteCounts = stripByteCounts->getData<uint32_t>(); 355 356 uint32_t total = 0; 357 for (size_t i = 0; i < static_cast<size_t>(count); ++i) { 358 total += byteCounts[i]; 359 } 360 return total; 361} 362 363String8 TiffIfd::toString() const { 364 size_t s = mEntries.size(); 365 String8 output; 366 output.appendFormat("[ifd: %x, num_entries: %zu, entries:\n", getId(), s); 367 for(size_t i = 0; i < mEntries.size(); ++i) { 368 output.append("\t"); 369 output.append(mEntries[i]->toString()); 370 output.append("\n"); 371 } 372 output.append(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0)); 373 return output; 374} 375 376void TiffIfd::log() const { 377 size_t s = mEntries.size(); 378 ALOGI("[ifd: %x, num_entries: %zu, entries:\n", getId(), s); 379 for(size_t i = 0; i < s; ++i) { 380 ALOGI("\t%s", mEntries[i]->toString().string()); 381 } 382 ALOGI(", next_ifd: %x]", ((mNextIfd != NULL) ? mNextIfd->getId() : 0)); 383} 384 385} /*namespace img_utils*/ 386} /*namespace android*/ 387