TiffIfd.cpp revision 4510de26e5361f3a9f07057ec6f26483c888c1fa
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