Flutter Engine
The Flutter Engine
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
16namespace SkTiff {
17
18constexpr size_t kSizeEntry = 12;
19constexpr size_t kSizeShort = 2;
20constexpr size_t kSizeLong = 4;
21
22bool ImageFileDirectory::IsValidType(uint16_t type) { return type >= 1 && type <= 12; }
23
24size_t ImageFileDirectory::BytesForType(uint16_t type) {
25 switch (type) {
27 return 1;
29 return 1;
31 return kSizeShort;
33 return kSizeLong;
35 return 8;
36 case kTypeSignedByte:
37 return 1;
38 case kTypeUndefined:
39 return 1;
41 return kSizeShort;
42 case kTypeSignedLong:
43 return kSizeLong;
45 return 8;
47 return 4;
49 return 8;
50 }
51 return 0;
52}
53
54// Helper function for computing the address of an entry.
55static const uint8_t* get_entry_address(const SkData* data,
56 uint32_t ifdOffset,
57 uint16_t entryIndex) {
58 return data->bytes() + // Base address
59 ifdOffset + // IFD offset
60 kSizeShort + // IFD number of entries
61 kSizeEntry * entryIndex; // Entries
62}
63
64// Return true if the IFD starting at |ifdOffset| contains valid number of entries (that doesn't
65// overrun |data|).
66static bool validate_ifd(const SkData* data,
67 bool littleEndian,
68 uint32_t ifdOffset,
69 bool allowTruncated,
70 uint16_t* outNumEntries,
71 uint32_t* outNextIfdOffset) {
72 const uint8_t* dataCurrent = data->bytes();
73 size_t dataSize = data->size();
74
75 // Seek to the IFD offset.
76 if (dataSize < ifdOffset) {
77 SkCodecPrintf("IFD offset is too large.\n");
78 return false;
79 }
80 dataCurrent += ifdOffset;
81 dataSize -= ifdOffset;
82
83 // Read the number of entries.
84 if (dataSize < kSizeShort) {
85 SkCodecPrintf("Insufficient space to store number of entries.\n");
86 return false;
87 }
88 uint16_t numEntries = get_endian_short(dataCurrent, littleEndian);
89 dataCurrent += kSizeShort;
90 dataSize -= kSizeShort;
91
92 // Check that there is enough space for all entries.
93 if (dataSize < kSizeEntry * numEntries) {
94 SkCodecPrintf("Insufficient space (%u) to store all %u entries.\n",
95 static_cast<uint32_t>(data->size()),
96 numEntries);
97 if (allowTruncated) {
98 // Set the number of entries to the number of entries that can be fully read, and set
99 // the next IFD offset to 0 (indicating that there is no next IFD).
100 *outNumEntries = dataSize / kSizeEntry;
101 *outNextIfdOffset = 0;
102 return true;
103 }
104 return false;
105 }
106
107 // Save the number of entries.
108 *outNumEntries = numEntries;
109
110 // Seek past the entries.
111 dataCurrent += kSizeEntry * numEntries;
112 dataSize -= kSizeEntry * numEntries;
113
114 // Read the next IFD offset.
115 if (dataSize < kSizeLong) {
116 SkCodecPrintf("Insufficient space to store next IFD offset.\n");
117 if (allowTruncated) {
118 // Set the next IFD offset to 0 (indicating that there is no next IFD).
119 *outNextIfdOffset = 0;
120 return true;
121 }
122 return false;
123 }
124
125 // Save the next IFD offset.
126 *outNextIfdOffset = get_endian_int(dataCurrent, littleEndian);
127 return true;
128}
129
131 bool* outLittleEndian,
132 uint32_t* outIfdOffset) {
133 // Read the endianness (4 bytes) and IFD offset (4 bytes).
134 if (data->size() < 8) {
135 SkCodecPrintf("Tiff header must be at least 8 bytes.\n");
136 return false;
137 }
138 if (!is_valid_endian_marker(data->bytes(), outLittleEndian)) {
139 SkCodecPrintf("Tiff header had invalid endian marker 0x%x,0x%x,0x%x,0x%x.\n",
140 data->bytes()[0],
141 data->bytes()[1],
142 data->bytes()[2],
143 data->bytes()[3]);
144 return false;
145 }
146 *outIfdOffset = get_endian_int(data->bytes() + 4, *outLittleEndian);
147 return true;
148}
149
150std::unique_ptr<ImageFileDirectory> ImageFileDirectory::MakeFromOffset(sk_sp<SkData> data,
151 bool littleEndian,
152 uint32_t ifdOffset,
153 bool allowTruncated) {
154 uint16_t numEntries = 0;
155 uint32_t nextOffset = 0;
156 if (!validate_ifd(
157 data.get(), littleEndian, ifdOffset, allowTruncated, &numEntries, &nextOffset)) {
158 SkCodecPrintf("Failed to validate IFD.\n");
159 return nullptr;
160 }
161 return std::unique_ptr<ImageFileDirectory>(new ImageFileDirectory(
162 std::move(data), littleEndian, ifdOffset, numEntries, nextOffset));
163}
164
165ImageFileDirectory::ImageFileDirectory(sk_sp<SkData> data,
166 bool littleEndian,
167 uint32_t offset,
168 uint16_t numEntries,
169 uint32_t nextIfdOffset)
170 : fData(std::move(data))
171 , fLittleEndian(littleEndian)
172 , fOffset(offset)
173 , fNumEntries(numEntries)
174 , fNextIfdOffset(nextIfdOffset) {}
175
176uint16_t ImageFileDirectory::getEntryTag(uint16_t entryIndex) const {
177 const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
178 return get_endian_short(entry, fLittleEndian);
179}
180
181bool ImageFileDirectory::getEntryRawData(uint16_t entryIndex,
182 uint16_t* outTag,
183 uint16_t* outType,
184 uint32_t* outCount,
185 const uint8_t** outData,
186 size_t* outDataSize) const {
187 const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
188
189 // Read the tag
190 const uint16_t tag = get_endian_short(entry, fLittleEndian);
191 entry += kSizeShort;
192
193 // Read the type.
194 const uint16_t type = get_endian_short(entry, fLittleEndian);
195 entry += kSizeShort;
196 if (!IsValidType(type)) {
197 return false;
198 }
199
200 // Read the count.
201 const uint32_t count = get_endian_int(entry, fLittleEndian);
202 entry += kSizeLong;
203
204 // If the entry fits in the remaining 4 bytes, use that.
205 const size_t entryDataBytes = BytesForType(type) * count;
206 const uint8_t* entryData = nullptr;
207 if (entryDataBytes <= kSizeLong) {
208 entryData = entry;
209 } else {
210 // Otherwise, the next 4 bytes specify an offset where the data can be found.
211 const uint32_t entryDataOffset = get_endian_int(entry, fLittleEndian);
212 if (fData->size() < entryDataOffset || fData->size() - entryDataOffset < entryDataBytes) {
213 return false;
214 }
215 entryData = fData->bytes() + entryDataOffset;
216 }
217
218 if (outTag) *outTag = tag;
219 if (outType) *outType = type;
220 if (outCount) *outCount = count;
221 if (outData) *outData = entryData;
222 if (outDataSize) *outDataSize = entryDataBytes;
223 return true;
224}
225
227 uint16_t type = 0;
228 uint32_t count = 0;
229 const uint8_t* data = nullptr;
230 size_t size = 0;
231 if (!getEntryRawData(entryIndex, nullptr, &type, &count, &data, &size)) {
232 return nullptr;
233 }
234 if (type != kTypeUndefined) {
235 return nullptr;
236 }
237 return SkData::MakeSubset(fData.get(), data - fData->bytes(), size);
238}
239
240bool ImageFileDirectory::getEntryValuesGeneric(uint16_t entryIndex,
241 uint16_t type,
242 uint32_t count,
243 void* values) const {
244 uint16_t entryType = 0;
245 uint32_t entryCount = 0;
246 const uint8_t* entryData = nullptr;
247 if (!getEntryRawData(entryIndex, nullptr, &entryType, &entryCount, &entryData, nullptr)) {
248 return false;
249 }
250 if (type != entryType) {
251 return false;
252 }
253 if (count != entryCount) {
254 return false;
255 }
256 for (uint32_t i = 0; i < count; ++i) {
257 const uint8_t* data = entryData + i * BytesForType(kTypeUnsignedLong);
258 switch (type) {
260 reinterpret_cast<uint16_t*>(values)[i] = get_endian_short(data, fLittleEndian);
261 break;
263 reinterpret_cast<uint32_t*>(values)[i] = get_endian_int(data, fLittleEndian);
264 break;
265 case kTypeSignedRational: {
266 uint32_t numerator = get_endian_int(data, fLittleEndian);
267 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
268 if (denominator == 0) {
269 // The TIFF specification does not indicate a behavior when the denominator is
270 // zero. The behavior of returning zero for a denominator of zero is a
271 // preservation of the behavior introduced in https://crrev.com/767874.
272 reinterpret_cast<float*>(values)[i] = 0;
273 } else {
274 reinterpret_cast<float*>(values)[i] =
275 numerator / static_cast<float>(denominator);
276 }
277 break;
278 }
280 uint32_t numerator = get_endian_int(data, fLittleEndian);
281 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
282 if (denominator == 0) {
283 // See comments in kTypeSignedRational.
284 reinterpret_cast<float*>(values)[i] = 0.f;
285 } else {
286 reinterpret_cast<float*>(values)[i] =
287 numerator / static_cast<float>(denominator);
288 }
289 break;
290 }
291 default:
292 SkCodecPrintf("Unsupported type %u\n", type);
293 return false;
294 break;
295 }
296 }
297 return true;
298}
299
300} // namespace SkTiff
int count
Definition: FontMgrTest.cpp:50
static uint32_t get_endian_int(const uint8_t *data, bool littleEndian)
Definition: SkCodecPriv.h:202
static uint16_t get_endian_short(const uint8_t *data, bool littleEndian)
Definition: SkCodecPriv.h:194
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
static bool is_valid_endian_marker(const uint8_t *data, bool *isLittleEndian)
Definition: SkCodecPriv.h:184
GLenum type
Definition: SkData.h:25
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
sk_sp< SkData > getEntryUndefinedData(uint16_t entryIndex) const
static bool ParseHeader(const SkData *data, bool *outLittleEndian, uint32_t *outIfdOffset)
static std::unique_ptr< ImageFileDirectory > MakeFromOffset(sk_sp< SkData > data, bool littleEndian, uint32_t ifdOffset, bool allowTruncated=false)
uint16_t getEntryTag(uint16_t entryIndex) const
T * get() const
Definition: SkRefCnt.h:303
constexpr size_t kSizeEntry
constexpr size_t kSizeLong
constexpr uint16_t kTypeDoubleFloat
Definition: SkTiffUtility.h:37
constexpr uint16_t kTypeUnsignedLong
Definition: SkTiffUtility.h:29
constexpr size_t kSizeShort
constexpr uint16_t kTypeSignedLong
Definition: SkTiffUtility.h:34
constexpr uint16_t kTypeUnsignedShort
Definition: SkTiffUtility.h:28
constexpr uint16_t kTypeSignedByte
Definition: SkTiffUtility.h:31
static bool validate_ifd(const SkData *data, bool littleEndian, uint32_t ifdOffset, bool allowTruncated, uint16_t *outNumEntries, uint32_t *outNextIfdOffset)
constexpr uint16_t kTypeUndefined
Definition: SkTiffUtility.h:32
constexpr uint16_t kTypeUnsignedByte
Definition: SkTiffUtility.h:26
constexpr uint16_t kTypeAsciiString
Definition: SkTiffUtility.h:27
constexpr uint16_t kTypeSignedShort
Definition: SkTiffUtility.h:33
static const uint8_t * get_entry_address(const SkData *data, uint32_t ifdOffset, uint16_t entryIndex)
constexpr uint16_t kTypeSingleFloat
Definition: SkTiffUtility.h:36
constexpr uint16_t kTypeSignedRational
Definition: SkTiffUtility.h:35
constexpr uint16_t kTypeUnsignedRational
Definition: SkTiffUtility.h:30
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
Definition: ref_ptr.h:256
SeparatedVector2 offset
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63