Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkTiffUtility.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
10#include "include/core/SkData.h"
12
13#include <cstddef>
14#include <utility>
15
16constexpr size_t kSizeEntry = 12;
17constexpr size_t kSizeShort = 2;
18constexpr size_t kSizeLong = 4;
19
20bool SkTiffImageFileDirectory::IsValidType(uint16_t type) { return type >= 1 && type <= 12; }
21
22size_t SkTiffImageFileDirectory::BytesForType(uint16_t type) {
23 switch (type) {
24 case kTypeUnsignedByte:
25 return 1;
26 case kTypeAsciiString:
27 return 1;
28 case kTypeUnsignedShort:
29 return kSizeShort;
30 case kTypeUnsignedLong:
31 return kSizeLong;
32 case kTypeUnsignedRational:
33 return 8;
34 case kTypeSignedByte:
35 return 1;
36 case kTypeUndefined:
37 return 1;
38 case kTypeSignedShort:
39 return kSizeShort;
40 case kTypeSignedLong:
41 return kSizeLong;
42 case kTypeSignedRational:
43 return 8;
44 case kTypeSingleFloat:
45 return 4;
46 case kTypeDoubleFloat:
47 return 8;
48 }
49 return 0;
50}
51
52// Helper function for computing the address of an entry.
53static const uint8_t* get_entry_address(const SkData* data,
54 uint32_t ifdOffset,
55 uint16_t entryIndex) {
56 return data->bytes() + // Base address
57 ifdOffset + // IFD offset
58 kSizeShort + // IFD number of entries
59 kSizeEntry * entryIndex; // Entries
60}
61
62// Return true if the IFD starting at |ifdOffset| contains valid number of entries (that doesn't
63// overrun |data|).
64static bool validate_ifd(const SkData* data,
65 bool littleEndian,
66 uint32_t ifdOffset,
67 uint16_t* outNumEntries,
68 uint32_t* outNextIfdOffset) {
69 const uint8_t* dataCurrent = data->bytes();
70 size_t dataSize = data->size();
71
72 // Seek to the IFD offset.
73 if (dataSize < ifdOffset) {
74 SkCodecPrintf("IFD offset is too large.\n");
75 return false;
76 }
77 dataCurrent += ifdOffset;
78 dataSize -= ifdOffset;
79
80 // Read the number of entries.
81 if (dataSize < kSizeShort) {
82 SkCodecPrintf("Insufficient space to store number of entries.\n");
83 return false;
84 }
85 uint16_t numEntries = get_endian_short(dataCurrent, littleEndian);
86 dataCurrent += kSizeShort;
87 dataSize -= kSizeShort;
88
89 // Check that there is enough space for all entries.
90 if (dataSize < kSizeEntry * numEntries) {
91 SkCodecPrintf("Insufficient space (%u) to store all %u entries.\n",
92 static_cast<uint32_t>(data->size()),
93 numEntries);
94 return false;
95 }
96
97 // Save the number of entries.
98 *outNumEntries = numEntries;
99
100 // Seek past the entries.
101 dataCurrent += kSizeEntry * numEntries;
102 dataSize -= kSizeEntry * numEntries;
103
104 // Read the next IFD offset.
105 if (dataSize < kSizeLong) {
106 SkCodecPrintf("Insufficient space to store next IFD offset.\n");
107 return false;
108 }
109
110 // Save the next IFD offset.
111 *outNextIfdOffset = get_endian_int(dataCurrent, littleEndian);
112 return true;
113}
114
116 bool* outLittleEndian,
117 uint32_t* outIfdOffset) {
118 // Read the endianness (4 bytes) and IFD offset (4 bytes).
119 if (data->size() < 8) {
120 SkCodecPrintf("Tiff header must be at least 8 bytes.\n");
121 return false;
122 }
123 if (!is_valid_endian_marker(data->bytes(), outLittleEndian)) {
124 SkCodecPrintf("Tiff header had invalid endian marker 0x%x,0x%x,0x%x,0x%x.\n",
125 data->bytes()[0],
126 data->bytes()[1],
127 data->bytes()[2],
128 data->bytes()[3]);
129 return false;
130 }
131 *outIfdOffset = get_endian_int(data->bytes() + 4, *outLittleEndian);
132 return true;
133}
134
135std::unique_ptr<SkTiffImageFileDirectory> SkTiffImageFileDirectory::MakeFromOffset(
136 sk_sp<SkData> data, bool littleEndian, uint32_t ifdOffset) {
137 uint16_t numEntries = 0;
138 uint32_t nextOffset = 0;
139 if (!validate_ifd(data.get(), littleEndian, ifdOffset, &numEntries, &nextOffset)) {
140 SkCodecPrintf("Failed to validate IFD.\n");
141 return nullptr;
142 }
143 return std::unique_ptr<SkTiffImageFileDirectory>(new SkTiffImageFileDirectory(
144 std::move(data), littleEndian, ifdOffset, numEntries, nextOffset));
145}
146
147SkTiffImageFileDirectory::SkTiffImageFileDirectory(sk_sp<SkData> data,
148 bool littleEndian,
149 uint32_t offset,
150 uint16_t numEntries,
151 uint32_t nextIfdOffset)
152 : fData(std::move(data))
153 , fLittleEndian(littleEndian)
154 , fOffset(offset)
155 , fNumEntries(numEntries)
156 , fNextIfdOffset(nextIfdOffset) {}
157
158uint16_t SkTiffImageFileDirectory::getEntryTag(uint16_t entryIndex) const {
159 const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
160 return get_endian_short(entry, fLittleEndian);
161}
162
163bool SkTiffImageFileDirectory::getEntryRawData(uint16_t entryIndex,
164 uint16_t* outTag,
165 uint16_t* outType,
166 uint32_t* outCount,
167 const uint8_t** outData,
168 size_t* outDataSize) const {
169 const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
170
171 // Read the tag
172 const uint16_t tag = get_endian_short(entry, fLittleEndian);
173 entry += kSizeShort;
174
175 // Read the type.
176 const uint16_t type = get_endian_short(entry, fLittleEndian);
177 entry += kSizeShort;
178 if (!IsValidType(type)) {
179 return false;
180 }
181
182 // Read the count.
183 const uint32_t count = get_endian_int(entry, fLittleEndian);
184 entry += kSizeLong;
185
186 // If the entry fits in the remaining 4 bytes, use that.
187 const size_t entryDataBytes = BytesForType(type) * count;
188 const uint8_t* entryData = nullptr;
189 if (entryDataBytes <= kSizeLong) {
190 entryData = entry;
191 } else {
192 // Otherwise, the next 4 bytes specify an offset where the data can be found.
193 const uint32_t entryDataOffset = get_endian_int(entry, fLittleEndian);
194 if (fData->size() < entryDataOffset || fData->size() - entryDataOffset < entryDataBytes) {
195 return false;
196 }
197 entryData = fData->bytes() + entryDataOffset;
198 }
199
200 if (outTag) *outTag = tag;
201 if (outType) *outType = type;
202 if (outCount) *outCount = count;
203 if (outData) *outData = entryData;
204 if (outDataSize) *outDataSize = entryDataBytes;
205 return true;
206}
207
209 uint16_t type = 0;
210 uint32_t count = 0;
211 const uint8_t* data = nullptr;
212 size_t size = 0;
213 if (!getEntryRawData(entryIndex, nullptr, &type, &count, &data, &size)) {
214 return nullptr;
215 }
216 if (type != kTypeUndefined) {
217 return nullptr;
218 }
219 return SkData::MakeSubset(fData.get(), data - fData->bytes(), size);
220}
221
222bool SkTiffImageFileDirectory::getEntryValuesGeneric(uint16_t entryIndex,
223 uint16_t type,
224 uint32_t count,
225 void* values) const {
226 uint16_t entryType = 0;
227 uint32_t entryCount = 0;
228 const uint8_t* entryData = nullptr;
229 if (!getEntryRawData(entryIndex, nullptr, &entryType, &entryCount, &entryData, nullptr)) {
230 return false;
231 }
232 if (type != entryType) {
233 return false;
234 }
235 if (count != entryCount) {
236 return false;
237 }
238 for (uint32_t i = 0; i < count; ++i) {
239 const uint8_t* data = entryData + i * BytesForType(kTypeUnsignedLong);
240 switch (type) {
241 case kTypeUnsignedShort:
242 reinterpret_cast<uint16_t*>(values)[i] = get_endian_short(data, fLittleEndian);
243 break;
244 case kTypeUnsignedLong:
245 reinterpret_cast<uint32_t*>(values)[i] = get_endian_int(data, fLittleEndian);
246 break;
247 case kTypeSignedRational: {
248 uint32_t numerator = get_endian_int(data, fLittleEndian);
249 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
250 if (denominator == 0) {
251 // The TIFF specification does not indicate a behavior when the denominator is
252 // zero. The behavior of returning zero for a denominator of zero is a
253 // preservation of the behavior introduced in https://crrev.com/767874.
254 reinterpret_cast<float*>(values)[i] = 0;
255 } else {
256 reinterpret_cast<float*>(values)[i] =
257 numerator / static_cast<float>(denominator);
258 }
259 break;
260 }
261 case kTypeUnsignedRational: {
262 uint32_t numerator = get_endian_int(data, fLittleEndian);
263 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
264 if (denominator == 0) {
265 // See comments in kTypeSignedRational.
266 reinterpret_cast<float*>(values)[i] = 0.f;
267 } else {
268 reinterpret_cast<float*>(values)[i] =
269 numerator / static_cast<float>(denominator);
270 }
271 break;
272 }
273 default:
274 SkCodecPrintf("Unsupported type %u\n", type);
275 return false;
276 break;
277 }
278 }
279 return true;
280}
int count
static uint32_t get_endian_int(const uint8_t *data, bool littleEndian)
static uint16_t get_endian_short(const uint8_t *data, bool littleEndian)
#define SkCodecPrintf(...)
Definition SkCodecPriv.h:23
static bool is_valid_endian_marker(const uint8_t *data, bool *isLittleEndian)
static bool validate_ifd(const SkData *data, bool littleEndian, uint32_t ifdOffset, uint16_t *outNumEntries, uint32_t *outNextIfdOffset)
constexpr size_t kSizeLong
static const uint8_t * get_entry_address(const SkData *data, uint32_t ifdOffset, uint16_t entryIndex)
constexpr size_t kSizeShort
constexpr size_t kSizeEntry
const uint8_t * bytes() const
Definition SkData.h:43
static sk_sp< SkData > MakeSubset(const SkData *src, size_t offset, size_t length)
Definition SkData.cpp:173
size_t size() const
Definition SkData.h:30
static std::unique_ptr< SkTiffImageFileDirectory > MakeFromOffset(sk_sp< SkData > data, bool littleEndian, uint32_t ifdOffset)
static bool ParseHeader(const SkData *data, bool *outLittleEndian, uint32_t *outIfdOffset)
sk_sp< SkData > getEntryUndefinedData(uint16_t entryIndex) const
uint16_t getEntryTag(uint16_t entryIndex) const
T * get() const
Definition SkRefCnt.h:303
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
Definition ref_ptr.h:256
Point offset