xref: /aosp_15_r20/external/skia/src/codec/SkJpegMultiPicture.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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