Home | History | Annotate | Download | only in src
      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 
     26 namespace android {
     27 namespace img_utils {
     28 
     29 TiffIfd::TiffIfd(uint32_t ifdId)
     30         : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
     31 
     32 TiffIfd::~TiffIfd() {}
     33 
     34 status_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 
     50 sp<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 
     59 void TiffIfd::removeEntry(uint16_t tag) {
     60     ssize_t index = mEntries.indexOfTag(tag);
     61     if (index >= 0) {
     62         mEntries.removeAt(index);
     63     }
     64 }
     65 
     66 
     67 void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
     68     mNextIfd = ifd;
     69 }
     70 
     71 sp<TiffIfd> TiffIfd::getNextIfd() const {
     72     return mNextIfd;
     73 }
     74 
     75 uint32_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 
    101 status_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 
    153 size_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 
    163 uint32_t TiffIfd::getId() const {
    164     return mIfdId;
    165 }
    166 
    167 uint32_t TiffIfd::getComparableValue() const {
    168     return mIfdId;
    169 }
    170 
    171 status_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 
    292 bool TiffIfd::uninitializedOffsets() const {
    293     return mStripOffsetsInitialized;
    294 }
    295 
    296 status_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 
    346 uint32_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 
    363 String8 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 
    376 void 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