Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkJpegMultiPicture.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#include "src/base/SkEndian.h"
17
18#include <cstring>
19
20constexpr size_t kMpEndianSize = 4;
21constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A};
22
23constexpr uint16_t kTypeUnsignedLong = 0x4;
24constexpr uint16_t kTypeUndefined = 0x7;
25
26constexpr uint32_t kIfdEntrySize = 12;
27constexpr uint32_t kIfdSerializedEntryCount = 3;
28
29constexpr uint16_t kVersionTag = 0xB000;
30constexpr uint32_t kVersionCount = 4;
31constexpr size_t kVersionSize = 4;
32constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
33
34constexpr uint16_t kNumberOfImagesTag = 0xB001;
35constexpr uint32_t kNumberOfImagesCount = 1;
36
37constexpr uint16_t kMPEntryTag = 0xB002;
38constexpr uint32_t kMPEntrySize = 16;
39
40constexpr uint32_t kMPEntryAttributeFormatMask = 0x7000000;
41constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
42
43constexpr uint32_t kMPEntryAttributeTypeMask = 0xFFFFFF;
44constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
45
46constexpr uint16_t kIndividualImageUniqueIDTag = 0xB003;
47constexpr uint32_t kIndividualImageUniqueIDSize = 33;
48
49constexpr uint16_t kTotalNumberCapturedFramesTag = 0xB004;
50
51std::unique_ptr<SkJpegMultiPictureParameters> SkJpegMultiPictureParameters::Make(
52 const sk_sp<const SkData>& segmentParameters) {
53 // Read the MP Format identifier starting after the APP2 Field Length. See Figure 4 of CIPA
54 // DC-x007-2009.
55 if (segmentParameters->size() < sizeof(kMpfSig)) {
56 return nullptr;
57 }
58 if (memcmp(segmentParameters->data(), kMpfSig, sizeof(kMpfSig)) != 0) {
59 return nullptr;
60 }
61 auto ifdData = SkData::MakeSubset(
62 segmentParameters.get(), sizeof(kMpfSig), segmentParameters->size() - sizeof(kMpfSig));
63 SkASSERT(ifdData);
64
65 // The rest of this function reads the structure described in Figure 6 of CIPA DC-x007-2009.
66 // Determine the endianness of the values in the structure. See Figure 5 (MP endian tag
67 // structure), and read the Index IFD offset.
68 bool littleEndian = false;
69 uint32_t ifdOffset = 0;
70 if (!SkTiffImageFileDirectory::ParseHeader(ifdData.get(), &littleEndian, &ifdOffset)) {
71 SkCodecPrintf("Failed to parse endian-ness and index IFD offset.\n");
72 return nullptr;
73 }
74
75 // Create the Index Image File Directory (Index IFD).
76 auto ifd = SkTiffImageFileDirectory::MakeFromOffset(ifdData, littleEndian, ifdOffset);
77 if (!ifd) {
78 SkCodecPrintf("Failed to create MP Index IFD offset.\n");
79 return nullptr;
80 }
81
82 // Read the number of tags in the Index IFD. See Table 3 (MP Index IFD Tags) for a description
83 // of all possible tags.
84 uint16_t tagCount = ifd->getNumEntries();
85
86 // We will extract the number of images from the tags.
87 uint32_t numberOfImages = 0;
88
89 // The data for the MP entries.
90 sk_sp<SkData> mpEntriesData;
91
92 // The MP Index IFD tags shall be specified in the order of their tag IDs (text from
93 // section 5.2.3), so keep track of the previous tag id read.
94 uint16_t previousTag = 0;
95 for (uint16_t idfEntryIndex = 0; idfEntryIndex < tagCount; ++idfEntryIndex) {
96 uint16_t tag = ifd->getEntryTag(idfEntryIndex);
97 if (previousTag >= tag) {
98 SkCodecPrintf("MPF tags not in order.\n");
99 return nullptr;
100 }
101 previousTag = tag;
102
103 switch (tag) {
104 case kVersionTag: {
105 // See 5.2.3.1: MP Format Version.
106 sk_sp<SkData> data = ifd->getEntryUndefinedData(idfEntryIndex);
107 if (!data) {
108 SkCodecPrintf("Version must be undefined type.\n");
109 return nullptr;
110 }
111 if (data->size() != kVersionSize) {
112 SkCodecPrintf("Version must be 4 bytes.\n");
113 return nullptr;
114 }
115 if (memcmp(data->data(), kVersionExpected, kVersionSize) != 0) {
116 SkCodecPrintf("Version value is not 0100.\n");
117 return nullptr;
118 }
119 break;
120 }
122 // See 5.2.3.2: Number of Images.
123 if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &numberOfImages)) {
124 SkCodecPrintf("Number of Images was not 1 unsigned long.\n");
125 }
126 if (numberOfImages < 1) {
127 SkCodecPrintf("Invalid number of images.\n");
128 return nullptr;
129 }
130 break;
131 case kMPEntryTag: {
132 // See 5.2.3.3: MP Entry.
133 mpEntriesData = ifd->getEntryUndefinedData(idfEntryIndex);
134 if (!mpEntriesData) {
135 SkCodecPrintf("MP entries data could not be extracted.\n");
136 return nullptr;
137 }
138 if (mpEntriesData->size() != kMPEntrySize * numberOfImages) {
139 SkCodecPrintf("MP entries data should be %ux%u bytes, was %u.\n",
141 numberOfImages,
142 static_cast<uint32_t>(mpEntriesData->size()));
143 return nullptr;
144 }
145 break;
146 }
148 // See 5.2.3.4: Individual Image Unique ID List.
149 // Validate that the count parameter is correct, but do not extract any other
150 // information.
151 sk_sp<SkData> data = ifd->getEntryUndefinedData(idfEntryIndex);
152 if (!data) {
153 SkCodecPrintf("Image Unique ID must be undefined type.\n");
154 return nullptr;
155 }
156 if (data->size() != kIndividualImageUniqueIDSize * numberOfImages) {
157 SkCodecPrintf("Invalid Image Unique ID count.\n");
158 return nullptr;
159 }
160 break;
161 }
163 // See 5.2.3.5: Total Number of Captured Frames.
164 uint32_t totalNumCapturedFrames = 0;
165 if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &totalNumCapturedFrames)) {
166 SkCodecPrintf("Total Number of Captures Frames was not 1 unsigned long.\n");
167 }
168 break;
169 }
170 default:
171 return nullptr;
172 }
173 }
174 if (!numberOfImages) {
175 SkCodecPrintf("Number of images must be greater than zero.\n");
176 return nullptr;
177 }
178 if (!mpEntriesData) {
179 SkCodecPrintf("MP Entry data was not present.\n");
180 return nullptr;
181 }
182
183 // Start to prepare the result that we will return.
184 auto result = std::make_unique<SkJpegMultiPictureParameters>();
185 result->images.resize(numberOfImages);
186
187 // The next IFD is the Attribute IFD offset. We will not read or validate the Attribute IFD.
188
189 // Parse the MP Entries data.
190 for (uint32_t i = 0; i < numberOfImages; ++i) {
191 const uint8_t* mpEntryData = mpEntriesData->bytes() + kMPEntrySize * i;
192 const uint32_t attribute = get_endian_int(mpEntryData + 0, littleEndian);
193 const uint32_t size = get_endian_int(mpEntryData + 4, littleEndian);
194 const uint32_t dataOffset = get_endian_int(mpEntryData + 8, littleEndian);
195
196 const bool isPrimary =
198 const bool isJpeg =
200
201 if (isPrimary != (i == 0)) {
202 SkCodecPrintf("Image must be primary iff it is the first image..\n");
203 return nullptr;
204 }
205 if (!isJpeg) {
206 SkCodecPrintf("Image format must be 0 (JPEG).\n");
207 return nullptr;
208 }
209
210 if (i == 0 && dataOffset != 0) {
211 SkCodecPrintf("First individual Image offset must be NULL.\n");
212 return nullptr;
213 }
214
215 result->images[i].dataOffset = dataOffset;
216 result->images[i].size = size;
217 }
218
219 return result;
220}
221
222// Return the number of bytes that will be written by SkJpegMultiPictureParametersSerialize, for a
223// given number of images.
224size_t multi_picture_params_serialized_size(size_t numberOfImages) {
225 return sizeof(kMpfSig) + // Signature
226 kMpEndianSize + // Endianness
227 sizeof(uint32_t) + // Index IFD Offset
228 sizeof(uint16_t) + // IFD entry count
229 kIfdSerializedEntryCount * kIfdEntrySize + // 3 IFD entries at 12 bytes each
230 sizeof(uint32_t) + // Attribute IFD offset
231 numberOfImages * kMPEntrySize; // MP Entries for each image
232}
233
234// Helper macros for SkJpegMultiPictureParameters::serialize. Byte-swap and write the specified
235// value, and return nullptr on failure.
236#define WRITE_UINT16(value) \
237 do { \
238 if (!s.write16(SkEndian_SwapBE16(value))) { \
239 return nullptr; \
240 } \
241 } while (0)
242
243#define WRITE_UINT32(value) \
244 do { \
245 if (!s.write32(SkEndian_SwapBE32(value))) { \
246 return nullptr; \
247 } \
248 } while (0)
249
251 // Write the MPF signature.
253 if (!s.write(kMpfSig, sizeof(kMpfSig))) {
254 SkCodecPrintf("Failed to write signature.\n");
255 return nullptr;
256 }
257
258 // We will always write as big-endian.
259 if (!s.write(kMpBigEndian, kMpEndianSize)) {
260 SkCodecPrintf("Failed to write endianness.\n");
261 return nullptr;
262 }
263 // Compute the number of images.
264 uint32_t numberOfImages = static_cast<uint32_t>(images.size());
265
266 // Set the Index IFD offset be the position after the endianness value and this offset.
267 constexpr uint32_t indexIfdOffset =
268 static_cast<uint16_t>(sizeof(kMpBigEndian) + sizeof(uint32_t));
269 WRITE_UINT32(indexIfdOffset);
270
271 // We will write 3 tags (version, number of images, MP entries).
272 constexpr uint32_t numberOfTags = 3;
273 WRITE_UINT16(numberOfTags);
274
275 // Write the version tag.
279 if (!s.write(kVersionExpected, kVersionSize)) {
280 SkCodecPrintf("Failed to write version.\n");
281 return nullptr;
282 }
283
284 // Write the number of images.
288 WRITE_UINT32(numberOfImages);
289
290 // Write the MP entries.
293 WRITE_UINT32(kMPEntrySize * numberOfImages);
294 const uint32_t mpEntryOffset =
295 static_cast<uint32_t>(s.bytesWritten() - // The bytes written so far
296 sizeof(kMpfSig) + // Excluding the MPF signature
297 sizeof(uint32_t) + // The 4 bytes for this offset
298 sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
299 WRITE_UINT32(mpEntryOffset);
300
301 // Write the attribute IFD offset (zero because we don't write it).
302 WRITE_UINT32(0);
303
304 // Write the MP entries.
305 for (size_t i = 0; i < images.size(); ++i) {
306 const auto& image = images[i];
307
308 uint32_t attribute = kMPEntryAttributeFormatJpeg;
309 if (i == 0) {
310 attribute |= kMPEntryAttributeTypePrimary;
311 }
312
313 WRITE_UINT32(attribute);
314 WRITE_UINT32(image.size);
315 WRITE_UINT32(image.dataOffset);
316 // Dependent image 1 and 2 entries are zero.
317 WRITE_UINT16(0);
318 WRITE_UINT16(0);
319 }
320
321 SkASSERT(s.bytesWritten() == multi_picture_params_serialized_size(images.size()));
322 return s.detachAsData();
323}
324
325#undef WRITE_UINT16
326#undef WRITE_UINT32
327
329 size_t mpSegmentOffset) {
330 // The value of zero is used by the primary image.
331 if (dataOffset == 0) {
332 return 0;
333 }
334 return mpSegmentOffset + // The offset to the marker
335 kJpegMarkerCodeSize + // The marker itself
336 kJpegSegmentParameterLengthSize + // The parameter length
337 sizeof(kMpfSig) + // The signature
338 dataOffset;
339}
#define SkASSERT(cond)
Definition SkAssert.h:116
static uint32_t get_endian_int(const uint8_t *data, bool littleEndian)
#define SkCodecPrintf(...)
Definition SkCodecPriv.h:23
static constexpr uint8_t kMpfSig[]
static constexpr size_t kJpegSegmentParameterLengthSize
static constexpr size_t kJpegMarkerCodeSize
constexpr uint32_t kIfdEntrySize
constexpr uint8_t kMpBigEndian[kMpEndianSize]
constexpr size_t kMpEndianSize
constexpr uint16_t kIndividualImageUniqueIDTag
constexpr uint32_t kNumberOfImagesCount
constexpr uint8_t kVersionExpected[kVersionSize]
constexpr uint16_t kVersionTag
constexpr uint16_t kTypeUndefined
constexpr uint16_t kMPEntryTag
constexpr uint32_t kVersionCount
constexpr uint32_t kIfdSerializedEntryCount
#define WRITE_UINT16(value)
constexpr size_t kVersionSize
constexpr uint32_t kMPEntryAttributeFormatJpeg
#define WRITE_UINT32(value)
constexpr uint16_t kTypeUnsignedLong
constexpr uint32_t kMPEntryAttributeTypeMask
constexpr uint32_t kMPEntrySize
constexpr uint16_t kTotalNumberCapturedFramesTag
constexpr uint32_t kMPEntryAttributeFormatMask
size_t multi_picture_params_serialized_size(size_t numberOfImages)
constexpr uint32_t kMPEntryAttributeTypePrimary
constexpr uint16_t kNumberOfImagesTag
constexpr uint32_t kIndividualImageUniqueIDSize
static sk_sp< SkData > MakeSubset(const SkData *src, size_t offset, size_t length)
Definition SkData.cpp:173
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)
T * get() const
Definition SkRefCnt.h:303
sk_sp< SkImage > image
Definition examples.cpp:29
struct MyStruct s
GAsyncResult * result
sk_sp< SkData > serialize() const
static size_t GetAbsoluteOffset(uint32_t dataOffset, size_t mpSegmentOffset)
static std::unique_ptr< SkJpegMultiPictureParameters > Make(const sk_sp< const SkData > &segmentParameters)