• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/codec/SkCodecPriv.h"
13 #include "src/codec/SkJpegConstants.h"
14 #include "src/codec/SkJpegSegmentScan.h"
15 #include "src/core/SkEndian.h"
16 
17 #include <cstring>
18 
19 constexpr size_t kMpEndianSize = 4;
20 constexpr uint8_t kMpLittleEndian[kMpEndianSize] = {0x49, 0x49, 0x2A, 0x00};
21 constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A};
22 
23 constexpr uint16_t kTypeLong = 0x4;
24 constexpr uint16_t kTypeUndefined = 0x7;
25 
26 constexpr uint32_t kTagSize = 12;
27 constexpr uint32_t kTagSerializedCount = 3;
28 
29 constexpr uint16_t kVersionTag = 0xB000;
30 constexpr uint16_t kVersionType = kTypeUndefined;
31 constexpr uint32_t kVersionCount = 4;
32 constexpr size_t kVersionSize = 4;
33 constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
34 
35 constexpr uint16_t kNumberOfImagesTag = 0xB001;
36 constexpr uint16_t kNumberOfImagesType = kTypeLong;
37 constexpr uint32_t kNumberOfImagesCount = 1;
38 
39 constexpr uint16_t kMPEntryTag = 0xB002;
40 constexpr uint16_t kMPEntryType = kTypeUndefined;
41 constexpr uint32_t kMPEntrySize = 16;
42 
43 constexpr uint32_t kMPEntryAttributeFormatMask = 0x7000000;
44 constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
45 
46 constexpr uint32_t kMPEntryAttributeTypeMask = 0xFFFFFF;
47 constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
48 
49 constexpr uint16_t kIndividualImageUniqueIDTag = 0xB003;
50 constexpr uint32_t kIndividualImageUniqueIDSize = 33;
51 
52 constexpr uint16_t kTotalNumberCapturedFramesTag = 0xB004;
53 constexpr uint32_t kTotalNumberCaptureFramesCount = 1;
54 
55 // Helper macro for SkJpegMultiPictureParameters::Make. Define the indicated variable VAR of type
56 // TYPE, and read it from the stream, performing any endian-ness conversions as needed. Also define
57 // the variable VAR##Bytes with the raw bytes (with no endian-ness conversion applied). If any
58 // errors are encountered, then return nullptr. The last void line is present to suppress unused
59 // variable warnings for parameters that we don't use.
60 #define DEFINE_AND_READ_UINT(TYPE, VAR)                                                  \
61     TYPE VAR = 0;                                                                        \
62     uint8_t VAR##Bytes[sizeof(TYPE)] = {0};                                              \
63     {                                                                                    \
64         if (!stream->read(VAR##Bytes, sizeof(TYPE))) {                                   \
65             return nullptr;                                                              \
66         }                                                                                \
67         for (size_t VAR##i = 0; VAR##i < sizeof(TYPE); ++VAR##i) {                       \
68             VAR *= 256;                                                                  \
69             VAR += VAR##Bytes[streamIsBigEndian ? VAR##i : (sizeof(TYPE) - VAR##i - 1)]; \
70         }                                                                                \
71     }                                                                                    \
72     (void)VAR
73 
Make(const sk_sp<const SkData> & segmentParameters)74 std::unique_ptr<SkJpegMultiPictureParameters> SkJpegMultiPictureParameters::Make(
75         const sk_sp<const SkData>& segmentParameters) {
76     // Read the MP Format identifier starting after the APP2 Field Length. See Figure 4 of CIPA
77     // DC-x007-2009.
78     if (segmentParameters->size() < sizeof(kMpfSig)) {
79         return nullptr;
80     }
81     if (memcmp(segmentParameters->data(), kMpfSig, sizeof(kMpfSig)) != 0) {
82         return nullptr;
83     }
84     std::unique_ptr<SkMemoryStream> stream =
85             SkMemoryStream::MakeDirect(segmentParameters->bytes() + sizeof(kMpfSig),
86                                        segmentParameters->size() - sizeof(kMpfSig));
87 
88     // The rest of this function reads the structure described in Figure 6 of CIPA DC-x007-2009.
89     // Determine the endianness of the values in the structure. See Figure 5 (MP endian tag
90     // structure).
91     bool streamIsBigEndian = false;
92     {
93         uint8_t endianTag[kMpEndianSize] = {0};
94         if (!stream->read(endianTag, kMpEndianSize)) {
95             SkCodecPrintf("Failed to read MP endian tag.\n");
96             return nullptr;
97         }
98         if (!memcmp(endianTag, kMpBigEndian, kMpEndianSize)) {
99             streamIsBigEndian = true;
100         } else if (!memcmp(endianTag, kMpLittleEndian, kMpEndianSize)) {
101             streamIsBigEndian = false;
102         } else {
103             SkCodecPrintf("MP endian tag was invalid.\n");
104             return nullptr;
105         }
106     }
107 
108     // Seek to the Index Image File Directory (Index IFD).
109     DEFINE_AND_READ_UINT(uint32_t, indexIfdOffset);
110     if (stream->getPosition() < indexIfdOffset) {
111         SkCodecPrintf("MP Index IFD offset moves backwards.\n");
112         return nullptr;
113     }
114     if (!stream->seek(indexIfdOffset)) {
115         SkCodecPrintf("Failed to seek to MPF IFD.\n");
116         return nullptr;
117     }
118 
119     // Read the number of tags in the Index IFD. See Table 3 (MP Index IFD Tags) for a description
120     // of all possible tags.
121     DEFINE_AND_READ_UINT(uint16_t, tagCount);
122 
123     // We will extract the number of images from the tags.
124     uint32_t numberOfImages = 0;
125 
126     // The offset to the MP entries. Zero is an invalid value.
127     uint32_t mpEntryOffset = 0;
128 
129     // The MP Index IFD tags shall be specified in the order of their tag IDs (text from
130     // section 5.2.3), so keep track of the previous tag id read.
131     uint16_t previousTagId = 0;
132     for (uint16_t tagIndex = 0; tagIndex < tagCount; ++tagIndex) {
133         DEFINE_AND_READ_UINT(uint16_t, tagId);
134         DEFINE_AND_READ_UINT(uint16_t, type);
135         DEFINE_AND_READ_UINT(uint32_t, count);
136         DEFINE_AND_READ_UINT(uint32_t, value);
137 
138         if (previousTagId >= tagId) {
139             SkCodecPrintf("MPF tags not in order.\n");
140             return nullptr;
141         }
142         previousTagId = tagId;
143 
144         switch (tagId) {
145             case kVersionTag:
146                 // See 5.2.3.1: MP Format Version.
147                 if (memcmp(valueBytes, kVersionExpected, kVersionSize) != 0) {
148                     SkCodecPrintf("Version value is not 0100.\n");
149                     return nullptr;
150                 }
151                 if (count != kVersionCount) {
152                     SkCodecPrintf("Version count not 4.\n");
153                     return nullptr;
154                 }
155                 break;
156             case kNumberOfImagesTag:
157                 // See 5.2.3.2: Number of Images.
158                 numberOfImages = value;
159                 if (type != kTypeLong) {
160                     SkCodecPrintf("Invalid Total Number of Captured Frames type.\n");
161                     return nullptr;
162                 }
163                 if (numberOfImages < 1) {
164                     SkCodecPrintf("Invalid number of images.\n");
165                     return nullptr;
166                 }
167                 break;
168             case kMPEntryTag: {
169                 // See 5.2.3.3: MP Entry.
170                 if (count != kMPEntrySize * numberOfImages) {
171                     SkCodecPrintf("Invalid MPEntry count.\n");
172                     return nullptr;
173                 }
174                 mpEntryOffset = value;
175                 break;
176             }
177             case kIndividualImageUniqueIDTag:
178                 // See 5.2.3.4: Individual Image Unique ID List.
179                 // Validate that the count parameter is correct, but do not extract any other
180                 // information.
181                 if (count != kIndividualImageUniqueIDSize * numberOfImages) {
182                     SkCodecPrintf("Invalid Image Unique ID count.\n");
183                     return nullptr;
184                 }
185                 break;
186             case kTotalNumberCapturedFramesTag:
187                 // See 5.2.3.5: Total Number of Captured Frames.
188                 if (type != kTypeLong) {
189                     SkCodecPrintf("Invalid Total Number of Captured Frames type.\n");
190                     return nullptr;
191                 }
192                 if (count != kTotalNumberCaptureFramesCount) {
193                     SkCodecPrintf("Invalid Total Number of Captured Frames count.\n");
194                     return nullptr;
195                 }
196                 break;
197             default:
198                 return nullptr;
199         }
200     }
201     if (!numberOfImages) {
202         SkCodecPrintf("Number of images must be greater than zero.\n");
203         return nullptr;
204     }
205     if (!mpEntryOffset) {
206         SkCodecPrintf("MP Entry tag was not present or had invalid offset.\n");
207         return nullptr;
208     }
209 
210     // Start to prepare the result that we will return.
211     auto result = std::make_unique<SkJpegMultiPictureParameters>();
212     result->images.resize(numberOfImages);
213 
214     // Read the Attribute IFD offset, and verify that it is zero (absent) or greater than our
215     // current offset. We will not read or validate the Attribute IFD.
216     DEFINE_AND_READ_UINT(uint32_t, attributeIfdOffset);
217     if (attributeIfdOffset > 0) {
218         if (stream->getPosition() < attributeIfdOffset) {
219             SkCodecPrintf("MP Attribute IFD offset moves backwards.\n");
220             return nullptr;
221         }
222     }
223 
224     // Read the MP Entries starting at the offset that we read earlier.
225     if (!stream->seek(mpEntryOffset)) {
226         SkCodecPrintf("Failed to seek to MP entries' offset.\n");
227         return nullptr;
228     }
229     for (uint32_t i = 0; i < numberOfImages; ++i) {
230         DEFINE_AND_READ_UINT(uint32_t, attribute);
231         const bool isPrimary =
232                 (attribute & kMPEntryAttributeTypeMask) == kMPEntryAttributeTypePrimary;
233         const bool isJpeg =
234                 (attribute & kMPEntryAttributeFormatMask) == kMPEntryAttributeFormatJpeg;
235 
236         if (isPrimary != (i == 0)) {
237             SkCodecPrintf("Image must be primary iff it is the first image..\n");
238             return nullptr;
239         }
240         if (!isJpeg) {
241             SkCodecPrintf("Image format must be 0 (JPEG).\n");
242             return nullptr;
243         }
244 
245         DEFINE_AND_READ_UINT(uint32_t, size);
246         DEFINE_AND_READ_UINT(uint32_t, dataOffset);
247         if (i == 0 && dataOffset != 0) {
248             SkCodecPrintf("First individual Image offset must be NULL.\n");
249             return nullptr;
250         }
251 
252         DEFINE_AND_READ_UINT(uint16_t, dependentImage1EntryNumber);
253         DEFINE_AND_READ_UINT(uint16_t, dependentImage2EntryNumber);
254         result->images[i].dataOffset = dataOffset;
255         result->images[i].size = size;
256     }
257 
258     return result;
259 }
260 
261 #undef DEFINE_AND_READ_UINT
262 
263 // Return the number of bytes that will be written by SkJpegMultiPictureParametersSerialize, for a
264 // given number of images.
multi_picture_params_serialized_size(size_t numberOfImages)265 size_t multi_picture_params_serialized_size(size_t numberOfImages) {
266     return sizeof(kMpfSig) +                 // Signature
267            kMpEndianSize +                   // Endianness
268            sizeof(uint32_t) +                // Index IFD Offset
269            sizeof(uint16_t) +                // Tag count
270            kTagSerializedCount * kTagSize +  // 3 tags at 12 bytes each
271            sizeof(uint32_t) +                // Attribute IFD offset
272            numberOfImages * kMPEntrySize;    // MP Entries for each image
273 }
274 
275 // Helper macros for SkJpegMultiPictureParameters::serialize. Byte-swap and write the specified
276 // value, and return nullptr on failure.
277 #define WRITE_UINT16(value)                         \
278     do {                                            \
279         if (!s.write16(SkEndian_SwapBE16(value))) { \
280             return nullptr;                         \
281         }                                           \
282     } while (0)
283 
284 #define WRITE_UINT32(value)                         \
285     do {                                            \
286         if (!s.write32(SkEndian_SwapBE32(value))) { \
287             return nullptr;                         \
288         }                                           \
289     } while (0)
290 
serialize() const291 sk_sp<SkData> SkJpegMultiPictureParameters::serialize() const {
292     // Write the MPF signature.
293     SkDynamicMemoryWStream s;
294     if (!s.write(kMpfSig, sizeof(kMpfSig))) {
295         SkCodecPrintf("Failed to write signature.\n");
296         return nullptr;
297     }
298 
299     // We will always write as big-endian.
300     if (!s.write(kMpBigEndian, kMpEndianSize)) {
301         SkCodecPrintf("Failed to write endianness.\n");
302         return nullptr;
303     }
304     // Compute the number of images.
305     uint32_t numberOfImages = static_cast<uint32_t>(images.size());
306 
307     // Set the Index IFD offset be the position after the endianness value and this offset.
308     constexpr uint32_t indexIfdOffset =
309             static_cast<uint16_t>(sizeof(kMpBigEndian) + sizeof(uint32_t));
310     WRITE_UINT32(indexIfdOffset);
311 
312     // We will write 3 tags (version, number of images, MP entries).
313     constexpr uint32_t numberOfTags = 3;
314     WRITE_UINT16(numberOfTags);
315 
316     // Write the version tag.
317     WRITE_UINT16(kVersionTag);
318     WRITE_UINT16(kVersionType);
319     WRITE_UINT32(kVersionCount);
320     if (!s.write(kVersionExpected, kVersionSize)) {
321         SkCodecPrintf("Failed to write version.\n");
322         return nullptr;
323     }
324 
325     // Write the number of images.
326     WRITE_UINT16(kNumberOfImagesTag);
327     WRITE_UINT16(kNumberOfImagesType);
328     WRITE_UINT32(kNumberOfImagesCount);
329     WRITE_UINT32(numberOfImages);
330 
331     // Write the MP entries.
332     WRITE_UINT16(kMPEntryTag);
333     WRITE_UINT16(kMPEntryType);
334     WRITE_UINT32(kMPEntrySize * numberOfImages);
335     const uint32_t mpEntryOffset =
336             static_cast<uint32_t>(s.bytesWritten() -  // The bytes written so far
337                                   sizeof(kMpfSig) +   // Excluding the MPF signature
338                                   sizeof(uint32_t) +  // The 4 bytes for this offset
339                                   sizeof(uint32_t));  // The 4 bytes for the attribute IFD offset.
340     WRITE_UINT32(mpEntryOffset);
341 
342     // Write the attribute IFD offset (zero because we don't write it).
343     WRITE_UINT32(0);
344 
345     // Write the MP entries.
346     for (size_t i = 0; i < images.size(); ++i) {
347         const auto& image = images[i];
348 
349         uint32_t attribute = kMPEntryAttributeFormatJpeg;
350         if (i == 0) {
351             attribute |= kMPEntryAttributeTypePrimary;
352         }
353 
354         WRITE_UINT32(attribute);
355         WRITE_UINT32(image.size);
356         WRITE_UINT32(image.dataOffset);
357         // Dependent image 1 and 2 entries are zero.
358         WRITE_UINT16(0);
359         WRITE_UINT16(0);
360     }
361 
362     SkASSERT(s.bytesWritten() == multi_picture_params_serialized_size(images.size()));
363     return s.detachAsData();
364 }
365 
366 #undef WRITE_UINT16
367 #undef WRITE_UINT32
368 
GetAbsoluteOffset(uint32_t dataOffset,size_t mpSegmentOffset)369 size_t SkJpegMultiPictureParameters::GetAbsoluteOffset(uint32_t dataOffset,
370                                                        size_t mpSegmentOffset) {
371     // The value of zero is used by the primary image.
372     if (dataOffset == 0) {
373         return 0;
374     }
375     return mpSegmentOffset +                  // The offset to the marker
376            kJpegMarkerCodeSize +              // The marker itself
377            kJpegSegmentParameterLengthSize +  // The parameter length
378            sizeof(kMpfSig) +                  // The signature
379            dataOffset;
380 }
381