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 
TiffIfd(uint32_t ifdId)29 TiffIfd::TiffIfd(uint32_t ifdId)
30         : mNextIfd(), mIfdId(ifdId), mStripOffsetsInitialized(false) {}
31 
~TiffIfd()32 TiffIfd::~TiffIfd() {}
33 
addEntry(const sp<TiffEntry> & entry)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 
getEntry(uint16_t tag) const50 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 
removeEntry(uint16_t tag)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 
setNextIfd(const sp<TiffIfd> & ifd)67 void TiffIfd::setNextIfd(const sp<TiffIfd>& ifd) {
68     mNextIfd = ifd;
69 }
70 
getNextIfd() const71 sp<TiffIfd> TiffIfd::getNextIfd() const {
72     return mNextIfd;
73 }
74 
checkAndGetOffset(uint32_t offset) const75 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 
writeData(uint32_t offset,EndianOutput * out) const101 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 
getSize() const153 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 
getId() const163 uint32_t TiffIfd::getId() const {
164     return mIfdId;
165 }
166 
getComparableValue() const167 uint32_t TiffIfd::getComparableValue() const {
168     return mIfdId;
169 }
170 
validateAndSetStripTags()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 
uninitializedOffsets() const292 bool TiffIfd::uninitializedOffsets() const {
293     return mStripOffsetsInitialized;
294 }
295 
setStripOffset(uint32_t offset)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 
getStripSize() const346 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 
toString() const363 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 
log() const376 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