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
8 #include "src/codec/SkJpegMultiPicture.h"
9
10 #include "include/core/SkData.h"
11 #include "include/core/SkStream.h"
12 #include "src/base/SkEndian.h"
13 #include "src/codec/SkCodecPriv.h"
14 #include "src/codec/SkJpegConstants.h"
15 #include "src/codec/SkJpegSegmentScan.h"
16 #include "src/codec/SkTiffUtility.h"
17 #include "src/core/SkStreamPriv.h"
18
19 #include <cstring>
20
21 constexpr uint16_t kVersionTag = 0xB000;
22 constexpr uint32_t kVersionCount = 4;
23 constexpr size_t kVersionSize = 4;
24 constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
25
26 constexpr uint16_t kNumberOfImagesTag = 0xB001;
27 constexpr uint32_t kNumberOfImagesCount = 1;
28
29 constexpr uint16_t kMPEntryTag = 0xB002;
30 constexpr uint32_t kMPEntrySize = 16;
31
32 constexpr uint32_t kMPEntryAttributeFormatMask = 0x7000000;
33 constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
34
35 constexpr uint32_t kMPEntryAttributeTypeMask = 0xFFFFFF;
36 constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
37
38 constexpr uint16_t kIndividualImageUniqueIDTag = 0xB003;
39 constexpr uint32_t kIndividualImageUniqueIDSize = 33;
40
41 constexpr uint16_t kTotalNumberCapturedFramesTag = 0xB004;
42
Make(const sk_sp<const SkData> & segmentParameters)43 std::unique_ptr<SkJpegMultiPictureParameters> SkJpegMultiPictureParameters::Make(
44 const sk_sp<const SkData>& segmentParameters) {
45 // Read the MP Format identifier starting after the APP2 Field Length. See Figure 4 of CIPA
46 // DC-x007-2009.
47 if (segmentParameters->size() < sizeof(kMpfSig)) {
48 return nullptr;
49 }
50 if (memcmp(segmentParameters->data(), kMpfSig, sizeof(kMpfSig)) != 0) {
51 return nullptr;
52 }
53 auto ifdData = SkData::MakeSubset(
54 segmentParameters.get(), sizeof(kMpfSig), segmentParameters->size() - sizeof(kMpfSig));
55 SkASSERT(ifdData);
56
57 // The rest of this function reads the structure described in Figure 6 of CIPA DC-x007-2009.
58 // Determine the endianness of the values in the structure. See Figure 5 (MP endian tag
59 // structure), and read the Index IFD offset.
60 bool littleEndian = false;
61 uint32_t ifdOffset = 0;
62 if (!SkTiff::ImageFileDirectory::ParseHeader(ifdData.get(), &littleEndian, &ifdOffset)) {
63 SkCodecPrintf("Failed to parse endian-ness and index IFD offset.\n");
64 return nullptr;
65 }
66
67 // Create the Index Image File Directory (Index IFD).
68 auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset(ifdData, littleEndian, ifdOffset);
69 if (!ifd) {
70 SkCodecPrintf("Failed to create MP Index IFD offset.\n");
71 return nullptr;
72 }
73
74 // Read the number of tags in the Index IFD. See Table 3 (MP Index IFD Tags) for a description
75 // of all possible tags.
76 uint16_t tagCount = ifd->getNumEntries();
77
78 // We will extract the number of images from the tags.
79 uint32_t numberOfImages = 0;
80
81 // The data for the MP entries.
82 sk_sp<SkData> mpEntriesData;
83
84 // The MP Index IFD tags shall be specified in the order of their tag IDs (text from
85 // section 5.2.3), so keep track of the previous tag id read.
86 uint16_t previousTag = 0;
87 for (uint16_t idfEntryIndex = 0; idfEntryIndex < tagCount; ++idfEntryIndex) {
88 uint16_t tag = ifd->getEntryTag(idfEntryIndex);
89 if (previousTag >= tag) {
90 SkCodecPrintf("MPF tags not in order.\n");
91 return nullptr;
92 }
93 previousTag = tag;
94
95 switch (tag) {
96 case kVersionTag: {
97 // See 5.2.3.1: MP Format Version.
98 sk_sp<SkData> data = ifd->getEntryUndefinedData(idfEntryIndex);
99 if (!data) {
100 SkCodecPrintf("Version must be undefined type.\n");
101 return nullptr;
102 }
103 if (data->size() != kVersionSize) {
104 SkCodecPrintf("Version must be 4 bytes.\n");
105 return nullptr;
106 }
107 if (memcmp(data->data(), kVersionExpected, kVersionSize) != 0) {
108 SkCodecPrintf("Version value is not 0100.\n");
109 return nullptr;
110 }
111 break;
112 }
113 case kNumberOfImagesTag:
114 // See 5.2.3.2: Number of Images.
115 if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &numberOfImages)) {
116 SkCodecPrintf("Number of Images was not 1 unsigned long.\n");
117 }
118 if (numberOfImages < 1) {
119 SkCodecPrintf("Invalid number of images.\n");
120 return nullptr;
121 }
122 break;
123 case kMPEntryTag: {
124 // See 5.2.3.3: MP Entry.
125 mpEntriesData = ifd->getEntryUndefinedData(idfEntryIndex);
126 if (!mpEntriesData) {
127 SkCodecPrintf("MP entries data could not be extracted.\n");
128 return nullptr;
129 }
130 if (mpEntriesData->size() != kMPEntrySize * numberOfImages) {
131 SkCodecPrintf("MP entries data should be %ux%u bytes, was %u.\n",
132 kMPEntrySize,
133 numberOfImages,
134 static_cast<uint32_t>(mpEntriesData->size()));
135 return nullptr;
136 }
137 break;
138 }
139 case kIndividualImageUniqueIDTag: {
140 // See 5.2.3.4: Individual Image Unique ID List.
141 // Validate that the count parameter is correct, but do not extract any other
142 // information.
143 sk_sp<SkData> data = ifd->getEntryUndefinedData(idfEntryIndex);
144 if (!data) {
145 SkCodecPrintf("Image Unique ID must be undefined type.\n");
146 return nullptr;
147 }
148 if (data->size() != kIndividualImageUniqueIDSize * numberOfImages) {
149 SkCodecPrintf("Invalid Image Unique ID count.\n");
150 return nullptr;
151 }
152 break;
153 }
154 case kTotalNumberCapturedFramesTag: {
155 // See 5.2.3.5: Total Number of Captured Frames.
156 uint32_t totalNumCapturedFrames = 0;
157 if (!ifd->getEntryUnsignedLong(idfEntryIndex, 1, &totalNumCapturedFrames)) {
158 SkCodecPrintf("Total Number of Captures Frames was not 1 unsigned long.\n");
159 }
160 break;
161 }
162 default:
163 return nullptr;
164 }
165 }
166 if (!numberOfImages) {
167 SkCodecPrintf("Number of images must be greater than zero.\n");
168 return nullptr;
169 }
170 if (!mpEntriesData) {
171 SkCodecPrintf("MP Entry data was not present.\n");
172 return nullptr;
173 }
174
175 // Start to prepare the result that we will return.
176 auto result = std::make_unique<SkJpegMultiPictureParameters>(numberOfImages);
177
178 // The next IFD is the Attribute IFD offset. We will not read or validate the Attribute IFD.
179
180 // Parse the MP Entries data.
181 for (uint32_t i = 0; i < numberOfImages; ++i) {
182 const uint8_t* mpEntryData = mpEntriesData->bytes() + kMPEntrySize * i;
183 const uint32_t attribute = get_endian_int(mpEntryData + 0, littleEndian);
184 const uint32_t size = get_endian_int(mpEntryData + 4, littleEndian);
185 const uint32_t dataOffset = get_endian_int(mpEntryData + 8, littleEndian);
186
187 const bool isPrimary =
188 (attribute & kMPEntryAttributeTypeMask) == kMPEntryAttributeTypePrimary;
189 const bool isJpeg =
190 (attribute & kMPEntryAttributeFormatMask) == kMPEntryAttributeFormatJpeg;
191
192 if (isPrimary != (i == 0)) {
193 SkCodecPrintf("Image must be primary iff it is the first image..\n");
194 return nullptr;
195 }
196 if (!isJpeg) {
197 SkCodecPrintf("Image format must be 0 (JPEG).\n");
198 return nullptr;
199 }
200
201 if (i == 0 && dataOffset != 0) {
202 SkCodecPrintf("First individual Image offset must be NULL.\n");
203 return nullptr;
204 }
205
206 result->images[i].dataOffset = dataOffset;
207 result->images[i].size = size;
208 }
209
210 return result;
211 }
212
serialize(uint32_t individualImageNumber) const213 sk_sp<SkData> SkJpegMultiPictureParameters::serialize(uint32_t individualImageNumber) const {
214 SkDynamicMemoryWStream s;
215
216 const uint32_t numberOfImages = static_cast<uint32_t>(images.size());
217
218 // Write the MPF signature.
219 s.write(kMpfSig, sizeof(kMpfSig));
220
221 // We will always write as big-endian.
222 s.write(SkTiff::kEndianBig, sizeof(SkTiff::kEndianBig));
223
224 // Set the first IFD offset be the position after the endianness value and this offset. This
225 // will be the MP Index IFD for the first individual image and the MP Attribute IFD for all
226 // other images.
227 constexpr uint32_t firstIfdOffset = sizeof(SkTiff::kEndianBig) + // Endian-ness
228 sizeof(uint32_t); // Index IFD offset
229 SkWStreamWriteU32BE(&s, firstIfdOffset);
230 SkASSERT(s.bytesWritten() - sizeof(kMpfSig) == firstIfdOffset);
231
232 if (individualImageNumber == 0) {
233 // The MP Index IFD will write 3 tags (version, number of images, and MP entries). See
234 // in Table 6 (MP Index IFD Tag Support Level) that these are the only mandatory entries.
235 const uint32_t mpIndexIfdNumberOfTags = 3;
236 SkWStreamWriteU16BE(&s, mpIndexIfdNumberOfTags);
237 } else {
238 // The MP Attribute IFD will write 1 tags (version). See in Table 7 (MP Attribute IFD Tag
239 // Support Level for Baseline MP Files) that no tags are required. If gainmap images support
240 // is added to CIPA DC-007, then some tags may be added and become mandatory.
241 const uint16_t mpAttributeIfdNumberOfTags = 1;
242 SkWStreamWriteU16BE(&s, mpAttributeIfdNumberOfTags);
243 }
244
245 // Write the version.
246 SkWStreamWriteU16BE(&s, kVersionTag);
247 SkWStreamWriteU16BE(&s, SkTiff::kTypeUndefined);
248 SkWStreamWriteU32BE(&s, kVersionCount);
249 s.write(kVersionExpected, kVersionSize);
250
251 if (individualImageNumber == 0) {
252 // Write the number of images.
253 SkWStreamWriteU16BE(&s, kNumberOfImagesTag);
254 SkWStreamWriteU16BE(&s, SkTiff::kTypeUnsignedLong);
255 SkWStreamWriteU32BE(&s, kNumberOfImagesCount);
256 SkWStreamWriteU32BE(&s, numberOfImages);
257
258 // Write the MP entries tag.
259 SkWStreamWriteU16BE(&s, kMPEntryTag);
260 SkWStreamWriteU16BE(&s, SkTiff::kTypeUndefined);
261 const uint32_t mpEntriesSize = kMPEntrySize * numberOfImages;
262 SkWStreamWriteU32BE(&s, mpEntriesSize);
263 const uint32_t mpEntryOffset = static_cast<uint32_t>(
264 s.bytesWritten() - // The bytes written so far
265 sizeof(kMpfSig) + // Excluding the MPF signature
266 sizeof(uint32_t) + // The 4 bytes for this offset
267 sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
268 SkWStreamWriteU32BE(&s, mpEntryOffset);
269
270 // Write the attribute IFD offset (zero because there is none).
271 SkWStreamWriteU32BE(&s, 0);
272
273 // Write the MP entries data.
274 SkASSERT(s.bytesWritten() - sizeof(kMpfSig) == mpEntryOffset);
275 for (size_t i = 0; i < images.size(); ++i) {
276 const auto& image = images[i];
277
278 uint32_t attribute = kMPEntryAttributeFormatJpeg;
279 if (i == 0) {
280 attribute |= kMPEntryAttributeTypePrimary;
281 }
282
283 SkWStreamWriteU32BE(&s, attribute);
284 SkWStreamWriteU32BE(&s, image.size);
285 SkWStreamWriteU32BE(&s, image.dataOffset);
286 // Dependent image 1 and 2 entries are zero.
287 SkWStreamWriteU16BE(&s, 0);
288 SkWStreamWriteU16BE(&s, 0);
289 }
290 } else {
291 // The non-first-individual-images do not have any further IFDs.
292 SkWStreamWriteU32BE(&s, 0);
293 }
294
295 return s.detachAsData();
296 }
297
mp_header_absolute_offset(size_t mpSegmentOffset)298 static size_t mp_header_absolute_offset(size_t mpSegmentOffset) {
299 return mpSegmentOffset + // The offset to the segment's marker
300 kJpegMarkerCodeSize + // The marker itself
301 kJpegSegmentParameterLengthSize + // The segment parameter length
302 sizeof(kMpfSig); // The {'M','P','F',0} signature
303 }
304
GetImageAbsoluteOffset(uint32_t dataOffset,size_t mpSegmentOffset)305 size_t SkJpegMultiPictureParameters::GetImageAbsoluteOffset(uint32_t dataOffset,
306 size_t mpSegmentOffset) {
307 // The value of zero is used by the primary image.
308 if (dataOffset == 0) {
309 return 0;
310 }
311 return mp_header_absolute_offset(mpSegmentOffset) + dataOffset;
312 }
313
GetImageDataOffset(size_t imageAbsoluteOffset,size_t mpSegmentOffset)314 uint32_t SkJpegMultiPictureParameters::GetImageDataOffset(size_t imageAbsoluteOffset,
315 size_t mpSegmentOffset) {
316 // The value of zero is used by the primary image.
317 if (imageAbsoluteOffset == 0) {
318 return 0;
319 }
320 return static_cast<uint32_t>(imageAbsoluteOffset - mp_header_absolute_offset(mpSegmentOffset));
321 }
322