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