xref: /aosp_15_r20/external/skia/src/codec/SkJpegMetadataDecoderImpl.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2024 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/SkJpegMetadataDecoderImpl.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecPriv.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkJpegConstants.h"
14*c8dee2aaSAndroid Build Coastguard Worker 
15*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
16*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
17*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
18*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkExif.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkGainmapInfo.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkXmp.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEndian.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkJpegMultiPicture.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkJpegSegmentScan.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkJpegSourceMgr.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkJpegXmp.h"
30*c8dee2aaSAndroid Build Coastguard Worker #else
31*c8dee2aaSAndroid Build Coastguard Worker struct SkGainmapInfo;
32*c8dee2aaSAndroid Build Coastguard Worker #endif  // SK_CODEC_DECODES_JPEG_GAINMAPS
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
getXmpMetadata() const35*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkXmp> SkJpegMetadataDecoderImpl::getXmpMetadata() const {
36*c8dee2aaSAndroid Build Coastguard Worker     std::vector<sk_sp<SkData>> decoderApp1Params;
37*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& marker : fMarkerList) {
38*c8dee2aaSAndroid Build Coastguard Worker         if (marker.fMarker == kXMPMarker) {
39*c8dee2aaSAndroid Build Coastguard Worker             decoderApp1Params.push_back(marker.fData);
40*c8dee2aaSAndroid Build Coastguard Worker         }
41*c8dee2aaSAndroid Build Coastguard Worker     }
42*c8dee2aaSAndroid Build Coastguard Worker     return SkJpegMakeXmp(decoderApp1Params);
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker 
45*c8dee2aaSAndroid Build Coastguard Worker // Extract the SkJpegMultiPictureParameters from this image (if they exist). If |sourceMgr| and
46*c8dee2aaSAndroid Build Coastguard Worker // |outMpParamsSegment| are non-nullptr, then also return the SkJpegSegment that the parameters came
47*c8dee2aaSAndroid Build Coastguard Worker // from (and return nullptr if one cannot be found).
find_mp_params(const SkJpegMarkerList & markerList,SkJpegSourceMgr * sourceMgr,SkJpegSegment * outMpParamsSegment)48*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<SkJpegMultiPictureParameters> find_mp_params(
49*c8dee2aaSAndroid Build Coastguard Worker         const SkJpegMarkerList& markerList,
50*c8dee2aaSAndroid Build Coastguard Worker         SkJpegSourceMgr* sourceMgr,
51*c8dee2aaSAndroid Build Coastguard Worker         SkJpegSegment* outMpParamsSegment) {
52*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkJpegMultiPictureParameters> mpParams;
53*c8dee2aaSAndroid Build Coastguard Worker     size_t skippedSegmentCount = 0;
54*c8dee2aaSAndroid Build Coastguard Worker 
55*c8dee2aaSAndroid Build Coastguard Worker     // Search though the libjpeg segments until we find a segment that parses as MP parameters. Keep
56*c8dee2aaSAndroid Build Coastguard Worker     // track of how many segments with the MPF marker we skipped over to get there.
57*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& marker : markerList) {
58*c8dee2aaSAndroid Build Coastguard Worker         if (marker.fMarker != kMpfMarker) {
59*c8dee2aaSAndroid Build Coastguard Worker             continue;
60*c8dee2aaSAndroid Build Coastguard Worker         }
61*c8dee2aaSAndroid Build Coastguard Worker         mpParams = SkJpegMultiPictureParameters::Make(marker.fData);
62*c8dee2aaSAndroid Build Coastguard Worker         if (mpParams) {
63*c8dee2aaSAndroid Build Coastguard Worker             break;
64*c8dee2aaSAndroid Build Coastguard Worker         }
65*c8dee2aaSAndroid Build Coastguard Worker         ++skippedSegmentCount;
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker     if (!mpParams) {
68*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
69*c8dee2aaSAndroid Build Coastguard Worker     }
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker     // If |sourceMgr| is not specified, then do not try to find the SkJpegSegment.
72*c8dee2aaSAndroid Build Coastguard Worker     if (!sourceMgr) {
73*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!outMpParamsSegment);
74*c8dee2aaSAndroid Build Coastguard Worker         return mpParams;
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     // Now, find the SkJpegSegmentScanner segment that corresponds to the libjpeg marker.
78*c8dee2aaSAndroid Build Coastguard Worker     // TODO(ccameron): It may be preferable to make SkJpegSourceMgr save segments with certain
79*c8dee2aaSAndroid Build Coastguard Worker     // markers to avoid this strangeness.
80*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& segment : sourceMgr->getAllSegments()) {
81*c8dee2aaSAndroid Build Coastguard Worker         if (segment.marker != kMpfMarker) {
82*c8dee2aaSAndroid Build Coastguard Worker             continue;
83*c8dee2aaSAndroid Build Coastguard Worker         }
84*c8dee2aaSAndroid Build Coastguard Worker         if (skippedSegmentCount == 0) {
85*c8dee2aaSAndroid Build Coastguard Worker             *outMpParamsSegment = segment;
86*c8dee2aaSAndroid Build Coastguard Worker             return mpParams;
87*c8dee2aaSAndroid Build Coastguard Worker         }
88*c8dee2aaSAndroid Build Coastguard Worker         skippedSegmentCount--;
89*c8dee2aaSAndroid Build Coastguard Worker     }
90*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker // Attempt to extract a gainmap image from a specified offset and size within the decoder's stream.
94*c8dee2aaSAndroid Build Coastguard Worker // Returns true only if the extracted gainmap image includes XMP metadata that specifies HDR gainmap
95*c8dee2aaSAndroid Build Coastguard Worker // rendering parameters.
extract_gainmap(SkJpegSourceMgr * decoderSource,size_t offset,size_t size,bool baseImageHasIsoVersion,bool baseImageHasAdobeXmp,std::optional<float> baseImageAppleHdrHeadroom,SkGainmapInfo & outInfo,sk_sp<SkData> & outData)96*c8dee2aaSAndroid Build Coastguard Worker static bool extract_gainmap(SkJpegSourceMgr* decoderSource,
97*c8dee2aaSAndroid Build Coastguard Worker                             size_t offset,
98*c8dee2aaSAndroid Build Coastguard Worker                             size_t size,
99*c8dee2aaSAndroid Build Coastguard Worker                             bool baseImageHasIsoVersion,
100*c8dee2aaSAndroid Build Coastguard Worker                             bool baseImageHasAdobeXmp,
101*c8dee2aaSAndroid Build Coastguard Worker                             std::optional<float> baseImageAppleHdrHeadroom,
102*c8dee2aaSAndroid Build Coastguard Worker                             SkGainmapInfo& outInfo,
103*c8dee2aaSAndroid Build Coastguard Worker                             sk_sp<SkData>& outData) {
104*c8dee2aaSAndroid Build Coastguard Worker     // Extract the SkData for this image.
105*c8dee2aaSAndroid Build Coastguard Worker     bool imageDataWasCopied = false;
106*c8dee2aaSAndroid Build Coastguard Worker     auto imageData = decoderSource->getSubsetData(offset, size, &imageDataWasCopied);
107*c8dee2aaSAndroid Build Coastguard Worker     if (!imageData) {
108*c8dee2aaSAndroid Build Coastguard Worker         SkCodecPrintf("Failed to extract MP image.\n");
109*c8dee2aaSAndroid Build Coastguard Worker         return false;
110*c8dee2aaSAndroid Build Coastguard Worker     }
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker     // Parse the potential gainmap image's metadata.
113*c8dee2aaSAndroid Build Coastguard Worker     SkJpegMetadataDecoderImpl metadataDecoder(imageData);
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker     // If this image identifies itself as a gainmap, then populate |info|.
116*c8dee2aaSAndroid Build Coastguard Worker     bool didPopulateInfo = false;
117*c8dee2aaSAndroid Build Coastguard Worker     SkGainmapInfo info;
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker     // Check for ISO 21496-1 gain map metadata.
120*c8dee2aaSAndroid Build Coastguard Worker     if (baseImageHasIsoVersion) {
121*c8dee2aaSAndroid Build Coastguard Worker         didPopulateInfo = SkGainmapInfo::Parse(
122*c8dee2aaSAndroid Build Coastguard Worker                 metadataDecoder.getISOGainmapMetadata(/*copyData=*/false).get(), info);
123*c8dee2aaSAndroid Build Coastguard Worker         if (didPopulateInfo && info.fGainmapMathColorSpace) {
124*c8dee2aaSAndroid Build Coastguard Worker             auto iccData = metadataDecoder.getICCProfileData(/*copyData=*/false);
125*c8dee2aaSAndroid Build Coastguard Worker             skcms_ICCProfile iccProfile;
126*c8dee2aaSAndroid Build Coastguard Worker             if (iccData && skcms_Parse(iccData->data(), iccData->size(), &iccProfile)) {
127*c8dee2aaSAndroid Build Coastguard Worker                 auto iccProfileSpace = SkColorSpace::Make(iccProfile);
128*c8dee2aaSAndroid Build Coastguard Worker                 if (iccProfileSpace) {
129*c8dee2aaSAndroid Build Coastguard Worker                     info.fGainmapMathColorSpace = std::move(iccProfileSpace);
130*c8dee2aaSAndroid Build Coastguard Worker                 }
131*c8dee2aaSAndroid Build Coastguard Worker             }
132*c8dee2aaSAndroid Build Coastguard Worker         }
133*c8dee2aaSAndroid Build Coastguard Worker     }
134*c8dee2aaSAndroid Build Coastguard Worker 
135*c8dee2aaSAndroid Build Coastguard Worker     if (!didPopulateInfo) {
136*c8dee2aaSAndroid Build Coastguard Worker         // The Adobe and Apple gain map metadata require XMP. Parse it now.
137*c8dee2aaSAndroid Build Coastguard Worker         auto xmp = metadataDecoder.getXmpMetadata();
138*c8dee2aaSAndroid Build Coastguard Worker         if (!xmp) {
139*c8dee2aaSAndroid Build Coastguard Worker             return false;
140*c8dee2aaSAndroid Build Coastguard Worker         }
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker         // Check for Adobe gain map metadata only if the base image specified hdrgm:Version="1.0".
143*c8dee2aaSAndroid Build Coastguard Worker         if (!didPopulateInfo && baseImageHasAdobeXmp) {
144*c8dee2aaSAndroid Build Coastguard Worker             didPopulateInfo = xmp->getGainmapInfoAdobe(&info);
145*c8dee2aaSAndroid Build Coastguard Worker         }
146*c8dee2aaSAndroid Build Coastguard Worker 
147*c8dee2aaSAndroid Build Coastguard Worker         // Next try for Apple gain map metadata. This does not require anything specific from the
148*c8dee2aaSAndroid Build Coastguard Worker         // base image.
149*c8dee2aaSAndroid Build Coastguard Worker         if (!didPopulateInfo && baseImageAppleHdrHeadroom.has_value()) {
150*c8dee2aaSAndroid Build Coastguard Worker             didPopulateInfo = xmp->getGainmapInfoApple(baseImageAppleHdrHeadroom.value(), &info);
151*c8dee2aaSAndroid Build Coastguard Worker         }
152*c8dee2aaSAndroid Build Coastguard Worker     }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     // If none of the formats identified itself as a gainmap and populated |info| then fail.
155*c8dee2aaSAndroid Build Coastguard Worker     if (!didPopulateInfo) {
156*c8dee2aaSAndroid Build Coastguard Worker         return false;
157*c8dee2aaSAndroid Build Coastguard Worker     }
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker     // This image is a gainmap.
160*c8dee2aaSAndroid Build Coastguard Worker     outInfo = info;
161*c8dee2aaSAndroid Build Coastguard Worker     if (imageDataWasCopied) {
162*c8dee2aaSAndroid Build Coastguard Worker         outData = imageData;
163*c8dee2aaSAndroid Build Coastguard Worker     } else {
164*c8dee2aaSAndroid Build Coastguard Worker         outData = SkData::MakeWithCopy(imageData->data(), imageData->size());
165*c8dee2aaSAndroid Build Coastguard Worker     }
166*c8dee2aaSAndroid Build Coastguard Worker     return true;
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker #endif
169*c8dee2aaSAndroid Build Coastguard Worker 
findGainmapImage(SkJpegSourceMgr * sourceMgr,sk_sp<SkData> & outData,SkGainmapInfo & outInfo) const170*c8dee2aaSAndroid Build Coastguard Worker bool SkJpegMetadataDecoderImpl::findGainmapImage(SkJpegSourceMgr* sourceMgr,
171*c8dee2aaSAndroid Build Coastguard Worker                                                  sk_sp<SkData>& outData,
172*c8dee2aaSAndroid Build Coastguard Worker                                                  SkGainmapInfo& outInfo) const {
173*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
174*c8dee2aaSAndroid Build Coastguard Worker     SkExif::Metadata baseExif;
175*c8dee2aaSAndroid Build Coastguard Worker     SkExif::Parse(baseExif, getExifMetadata(/*copyData=*/false).get());
176*c8dee2aaSAndroid Build Coastguard Worker     auto xmp = getXmpMetadata();
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker     // Determine if a support ISO 21496-1 gain map version is present in the base image.
179*c8dee2aaSAndroid Build Coastguard Worker     bool isoGainmapPresent =
180*c8dee2aaSAndroid Build Coastguard Worker             SkGainmapInfo::ParseVersion(getISOGainmapMetadata(/*copyData=*/false).get());
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     // Determine if Adobe HDR gain map is indicated in the base image.
183*c8dee2aaSAndroid Build Coastguard Worker     bool adobeGainmapPresent = xmp && xmp->getGainmapInfoAdobe(nullptr);
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker     // Attempt to locate the gainmap from the container XMP.
186*c8dee2aaSAndroid Build Coastguard Worker     size_t containerGainmapOffset = 0;
187*c8dee2aaSAndroid Build Coastguard Worker     size_t containerGainmapSize = 0;
188*c8dee2aaSAndroid Build Coastguard Worker     if (xmp && xmp->getContainerGainmapLocation(&containerGainmapOffset, &containerGainmapSize)) {
189*c8dee2aaSAndroid Build Coastguard Worker         const auto& segments = sourceMgr->getAllSegments();
190*c8dee2aaSAndroid Build Coastguard Worker         if (!segments.empty()) {
191*c8dee2aaSAndroid Build Coastguard Worker             const auto& lastSegment = segments.back();
192*c8dee2aaSAndroid Build Coastguard Worker             if (lastSegment.marker == kJpegMarkerEndOfImage) {
193*c8dee2aaSAndroid Build Coastguard Worker                 containerGainmapOffset += lastSegment.offset + kJpegMarkerCodeSize;
194*c8dee2aaSAndroid Build Coastguard Worker             }
195*c8dee2aaSAndroid Build Coastguard Worker         }
196*c8dee2aaSAndroid Build Coastguard Worker     }
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker     // Attempt to find MultiPicture parameters.
199*c8dee2aaSAndroid Build Coastguard Worker     SkJpegSegment mpParamsSegment;
200*c8dee2aaSAndroid Build Coastguard Worker     auto mpParams = find_mp_params(fMarkerList, sourceMgr, &mpParamsSegment);
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     // First, search through the Multi-Picture images.
203*c8dee2aaSAndroid Build Coastguard Worker     if (mpParams) {
204*c8dee2aaSAndroid Build Coastguard Worker         for (size_t mpImageIndex = 1; mpImageIndex < mpParams->images.size(); ++mpImageIndex) {
205*c8dee2aaSAndroid Build Coastguard Worker             size_t mpImageOffset = SkJpegMultiPictureParameters::GetImageAbsoluteOffset(
206*c8dee2aaSAndroid Build Coastguard Worker                     mpParams->images[mpImageIndex].dataOffset, mpParamsSegment.offset);
207*c8dee2aaSAndroid Build Coastguard Worker             size_t mpImageSize = mpParams->images[mpImageIndex].size;
208*c8dee2aaSAndroid Build Coastguard Worker 
209*c8dee2aaSAndroid Build Coastguard Worker             if (extract_gainmap(sourceMgr,
210*c8dee2aaSAndroid Build Coastguard Worker                                 mpImageOffset,
211*c8dee2aaSAndroid Build Coastguard Worker                                 mpImageSize,
212*c8dee2aaSAndroid Build Coastguard Worker                                 isoGainmapPresent,
213*c8dee2aaSAndroid Build Coastguard Worker                                 adobeGainmapPresent,
214*c8dee2aaSAndroid Build Coastguard Worker                                 baseExif.fHdrHeadroom,
215*c8dee2aaSAndroid Build Coastguard Worker                                 outInfo,
216*c8dee2aaSAndroid Build Coastguard Worker                                 outData)) {
217*c8dee2aaSAndroid Build Coastguard Worker                 // If the GContainer also suggested an offset and size, assert that we found the
218*c8dee2aaSAndroid Build Coastguard Worker                 // image that the GContainer suggested.
219*c8dee2aaSAndroid Build Coastguard Worker                 if (containerGainmapOffset) {
220*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(containerGainmapOffset == mpImageOffset);
221*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(containerGainmapSize == mpImageSize);
222*c8dee2aaSAndroid Build Coastguard Worker                 }
223*c8dee2aaSAndroid Build Coastguard Worker                 return true;
224*c8dee2aaSAndroid Build Coastguard Worker             }
225*c8dee2aaSAndroid Build Coastguard Worker         }
226*c8dee2aaSAndroid Build Coastguard Worker     }
227*c8dee2aaSAndroid Build Coastguard Worker 
228*c8dee2aaSAndroid Build Coastguard Worker     // Next, try the location suggested by the container XMP.
229*c8dee2aaSAndroid Build Coastguard Worker     if (containerGainmapOffset) {
230*c8dee2aaSAndroid Build Coastguard Worker         if (extract_gainmap(sourceMgr,
231*c8dee2aaSAndroid Build Coastguard Worker                             containerGainmapOffset,
232*c8dee2aaSAndroid Build Coastguard Worker                             containerGainmapSize,
233*c8dee2aaSAndroid Build Coastguard Worker                             /*baseImageHasIsoVersion=*/false,
234*c8dee2aaSAndroid Build Coastguard Worker                             adobeGainmapPresent,
235*c8dee2aaSAndroid Build Coastguard Worker                             /*baseImageAppleHdrHeadroom=*/std::nullopt,
236*c8dee2aaSAndroid Build Coastguard Worker                             outInfo,
237*c8dee2aaSAndroid Build Coastguard Worker                             outData)) {
238*c8dee2aaSAndroid Build Coastguard Worker             return true;
239*c8dee2aaSAndroid Build Coastguard Worker         }
240*c8dee2aaSAndroid Build Coastguard Worker         SkCodecPrintf("Failed to extract container-specified gainmap.\n");
241*c8dee2aaSAndroid Build Coastguard Worker     }
242*c8dee2aaSAndroid Build Coastguard Worker #endif
243*c8dee2aaSAndroid Build Coastguard Worker     return false;
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker /**
247*c8dee2aaSAndroid Build Coastguard Worker  * Return true if the specified SkJpegMarker has marker |targetMarker| and begins with the specified
248*c8dee2aaSAndroid Build Coastguard Worker  * signature.
249*c8dee2aaSAndroid Build Coastguard Worker  */
marker_has_signature(const SkJpegMarker & marker,const uint32_t targetMarker,const uint8_t * signature,size_t signatureSize)250*c8dee2aaSAndroid Build Coastguard Worker static bool marker_has_signature(const SkJpegMarker& marker,
251*c8dee2aaSAndroid Build Coastguard Worker                                  const uint32_t targetMarker,
252*c8dee2aaSAndroid Build Coastguard Worker                                  const uint8_t* signature,
253*c8dee2aaSAndroid Build Coastguard Worker                                  size_t signatureSize) {
254*c8dee2aaSAndroid Build Coastguard Worker     if (targetMarker != marker.fMarker) {
255*c8dee2aaSAndroid Build Coastguard Worker         return false;
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker     if (marker.fData->size() <= signatureSize) {
258*c8dee2aaSAndroid Build Coastguard Worker         return false;
259*c8dee2aaSAndroid Build Coastguard Worker     }
260*c8dee2aaSAndroid Build Coastguard Worker     if (memcmp(marker.fData->bytes(), signature, signatureSize) != 0) {
261*c8dee2aaSAndroid Build Coastguard Worker         return false;
262*c8dee2aaSAndroid Build Coastguard Worker     }
263*c8dee2aaSAndroid Build Coastguard Worker     return true;
264*c8dee2aaSAndroid Build Coastguard Worker }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker /*
267*c8dee2aaSAndroid Build Coastguard Worker  * Return metadata with a specific marker and signature.
268*c8dee2aaSAndroid Build Coastguard Worker  *
269*c8dee2aaSAndroid Build Coastguard Worker  * Search for segments that start with the specified targetMarker, followed by the specified
270*c8dee2aaSAndroid Build Coastguard Worker  * signature, followed by (optional) padding.
271*c8dee2aaSAndroid Build Coastguard Worker  *
272*c8dee2aaSAndroid Build Coastguard Worker  * Some types of metadata (e.g, ICC profiles) are too big to fit into a single segment's data (which
273*c8dee2aaSAndroid Build Coastguard Worker  * is limited to 64k), and come in multiple parts. For this type of data, bytesInIndex is >0. After
274*c8dee2aaSAndroid Build Coastguard Worker  * the signature comes bytesInIndex bytes (big endian) for the index of the segment's part, followed
275*c8dee2aaSAndroid Build Coastguard Worker  * by bytesInIndex bytes (big endian) for the total number of parts. If all parts are present,
276*c8dee2aaSAndroid Build Coastguard Worker  * stitch them together and return the combined result. Return failure if parts are absent, there
277*c8dee2aaSAndroid Build Coastguard Worker  * are duplicate parts, or parts disagree on the total number of parts.
278*c8dee2aaSAndroid Build Coastguard Worker  *
279*c8dee2aaSAndroid Build Coastguard Worker  * Visually, each segment is:
280*c8dee2aaSAndroid Build Coastguard Worker  * [|signatureSize| bytes containing |signature|]
281*c8dee2aaSAndroid Build Coastguard Worker  * [|signaturePadding| bytes that are unexamined]
282*c8dee2aaSAndroid Build Coastguard Worker  * [|bytesInIndex] bytes listing the segment index for multi-segment metadata]
283*c8dee2aaSAndroid Build Coastguard Worker  * [|bytesInIndex] bytes listing the segment count for multi-segment metadata]
284*c8dee2aaSAndroid Build Coastguard Worker  * [the returned data]
285*c8dee2aaSAndroid Build Coastguard Worker  *
286*c8dee2aaSAndroid Build Coastguard Worker  * If alwaysCopyData is true, then return a copy of the data. If alwaysCopyData is false, then
287*c8dee2aaSAndroid Build Coastguard Worker  * return a direct reference to the data pointed to by dinfo, if possible.
288*c8dee2aaSAndroid Build Coastguard Worker  */
read_metadata(const SkJpegMarkerList & markerList,const uint32_t targetMarker,const uint8_t * signature,size_t signatureSize,size_t signaturePadding,size_t bytesInIndex,bool alwaysCopyData)289*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkData> read_metadata(const SkJpegMarkerList& markerList,
290*c8dee2aaSAndroid Build Coastguard Worker                                    const uint32_t targetMarker,
291*c8dee2aaSAndroid Build Coastguard Worker                                    const uint8_t* signature,
292*c8dee2aaSAndroid Build Coastguard Worker                                    size_t signatureSize,
293*c8dee2aaSAndroid Build Coastguard Worker                                    size_t signaturePadding,
294*c8dee2aaSAndroid Build Coastguard Worker                                    size_t bytesInIndex,
295*c8dee2aaSAndroid Build Coastguard Worker                                    bool alwaysCopyData) {
296*c8dee2aaSAndroid Build Coastguard Worker     // Compute the total size of the entire header (signature plus padding plus index plus count),
297*c8dee2aaSAndroid Build Coastguard Worker     // since we'll use it often.
298*c8dee2aaSAndroid Build Coastguard Worker     const size_t headerSize = signatureSize + signaturePadding + 2 * bytesInIndex;
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker     // A map from part index to the data in each part.
301*c8dee2aaSAndroid Build Coastguard Worker     std::vector<sk_sp<SkData>> parts;
302*c8dee2aaSAndroid Build Coastguard Worker 
303*c8dee2aaSAndroid Build Coastguard Worker     // Running total of number of data in all parts.
304*c8dee2aaSAndroid Build Coastguard Worker     size_t partsTotalSize = 0;
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker     // Running total number of parts found.
307*c8dee2aaSAndroid Build Coastguard Worker     uint32_t foundPartCount = 0;
308*c8dee2aaSAndroid Build Coastguard Worker 
309*c8dee2aaSAndroid Build Coastguard Worker     // The expected number of parts (initialized at the first part we encounter).
310*c8dee2aaSAndroid Build Coastguard Worker     uint32_t expectedPartCount = 0;
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker     // Iterate through the image's segments.
313*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& marker : markerList) {
314*c8dee2aaSAndroid Build Coastguard Worker         // Skip segments that don't have the right marker or signature.
315*c8dee2aaSAndroid Build Coastguard Worker         if (!marker_has_signature(marker, targetMarker, signature, signatureSize)) {
316*c8dee2aaSAndroid Build Coastguard Worker             continue;
317*c8dee2aaSAndroid Build Coastguard Worker         }
318*c8dee2aaSAndroid Build Coastguard Worker 
319*c8dee2aaSAndroid Build Coastguard Worker         // Skip segments that are too small to include the index and count.
320*c8dee2aaSAndroid Build Coastguard Worker         const size_t dataLength = marker.fData->size();
321*c8dee2aaSAndroid Build Coastguard Worker         if (dataLength <= headerSize) {
322*c8dee2aaSAndroid Build Coastguard Worker             continue;
323*c8dee2aaSAndroid Build Coastguard Worker         }
324*c8dee2aaSAndroid Build Coastguard Worker 
325*c8dee2aaSAndroid Build Coastguard Worker         // Read this part's index and count as big-endian (if they are present, otherwise hard-code
326*c8dee2aaSAndroid Build Coastguard Worker         // them to 1).
327*c8dee2aaSAndroid Build Coastguard Worker         const uint8_t* data = marker.fData->bytes();
328*c8dee2aaSAndroid Build Coastguard Worker         uint32_t partIndex = 0;
329*c8dee2aaSAndroid Build Coastguard Worker         uint32_t partCount = 0;
330*c8dee2aaSAndroid Build Coastguard Worker         if (bytesInIndex == 0) {
331*c8dee2aaSAndroid Build Coastguard Worker             partIndex = 1;
332*c8dee2aaSAndroid Build Coastguard Worker             partCount = 1;
333*c8dee2aaSAndroid Build Coastguard Worker         } else {
334*c8dee2aaSAndroid Build Coastguard Worker             for (size_t i = 0; i < bytesInIndex; ++i) {
335*c8dee2aaSAndroid Build Coastguard Worker                 const size_t offset = signatureSize + signaturePadding;
336*c8dee2aaSAndroid Build Coastguard Worker                 partIndex = (partIndex << 8) + data[offset + i];
337*c8dee2aaSAndroid Build Coastguard Worker                 partCount = (partCount << 8) + data[offset + bytesInIndex + i];
338*c8dee2aaSAndroid Build Coastguard Worker             }
339*c8dee2aaSAndroid Build Coastguard Worker         }
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker         // A part count of 0 is invalid.
342*c8dee2aaSAndroid Build Coastguard Worker         if (!partCount) {
343*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Invalid marker part count zero\n");
344*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
345*c8dee2aaSAndroid Build Coastguard Worker         }
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker         // The indices must in the range 1, ..., count.
348*c8dee2aaSAndroid Build Coastguard Worker         if (partIndex <= 0 || partIndex > partCount) {
349*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Invalid marker index %u for count %u\n", partIndex, partCount);
350*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
351*c8dee2aaSAndroid Build Coastguard Worker         }
352*c8dee2aaSAndroid Build Coastguard Worker 
353*c8dee2aaSAndroid Build Coastguard Worker         // If this is the first marker we've encountered set the expected part count to its count.
354*c8dee2aaSAndroid Build Coastguard Worker         if (expectedPartCount == 0) {
355*c8dee2aaSAndroid Build Coastguard Worker             expectedPartCount = partCount;
356*c8dee2aaSAndroid Build Coastguard Worker             parts.resize(expectedPartCount);
357*c8dee2aaSAndroid Build Coastguard Worker         }
358*c8dee2aaSAndroid Build Coastguard Worker 
359*c8dee2aaSAndroid Build Coastguard Worker         // If this does not match the expected part count, then fail.
360*c8dee2aaSAndroid Build Coastguard Worker         if (partCount != expectedPartCount) {
361*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Conflicting marker counts %u vs %u\n", partCount, expectedPartCount);
362*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
363*c8dee2aaSAndroid Build Coastguard Worker         }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker         // Make an SkData directly referencing the decoder's data for this part.
366*c8dee2aaSAndroid Build Coastguard Worker         auto partData = SkData::MakeWithoutCopy(data + headerSize, dataLength - headerSize);
367*c8dee2aaSAndroid Build Coastguard Worker 
368*c8dee2aaSAndroid Build Coastguard Worker         // Fail if duplicates are found.
369*c8dee2aaSAndroid Build Coastguard Worker         if (parts[partIndex - 1]) {
370*c8dee2aaSAndroid Build Coastguard Worker             SkCodecPrintf("Duplicate parts for index %u of %u\n", partIndex, expectedPartCount);
371*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
372*c8dee2aaSAndroid Build Coastguard Worker         }
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker         // Save part in the map.
375*c8dee2aaSAndroid Build Coastguard Worker         partsTotalSize += partData->size();
376*c8dee2aaSAndroid Build Coastguard Worker         parts[partIndex - 1] = std::move(partData);
377*c8dee2aaSAndroid Build Coastguard Worker         foundPartCount += 1;
378*c8dee2aaSAndroid Build Coastguard Worker 
379*c8dee2aaSAndroid Build Coastguard Worker         // Stop as soon as we find all of the parts.
380*c8dee2aaSAndroid Build Coastguard Worker         if (foundPartCount == expectedPartCount) {
381*c8dee2aaSAndroid Build Coastguard Worker             break;
382*c8dee2aaSAndroid Build Coastguard Worker         }
383*c8dee2aaSAndroid Build Coastguard Worker     }
384*c8dee2aaSAndroid Build Coastguard Worker 
385*c8dee2aaSAndroid Build Coastguard Worker     // Return nullptr if we don't find the data (this is not an error).
386*c8dee2aaSAndroid Build Coastguard Worker     if (expectedPartCount == 0) {
387*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
388*c8dee2aaSAndroid Build Coastguard Worker     }
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker     // Fail if we don't have all of the parts.
391*c8dee2aaSAndroid Build Coastguard Worker     if (foundPartCount != expectedPartCount) {
392*c8dee2aaSAndroid Build Coastguard Worker         SkCodecPrintf("Incomplete set of markers (expected %u got %u)\n",
393*c8dee2aaSAndroid Build Coastguard Worker                       expectedPartCount,
394*c8dee2aaSAndroid Build Coastguard Worker                       foundPartCount);
395*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
396*c8dee2aaSAndroid Build Coastguard Worker     }
397*c8dee2aaSAndroid Build Coastguard Worker 
398*c8dee2aaSAndroid Build Coastguard Worker     // Return a direct reference to the data if there is only one part and we're allowed to.
399*c8dee2aaSAndroid Build Coastguard Worker     if (!alwaysCopyData && expectedPartCount == 1) {
400*c8dee2aaSAndroid Build Coastguard Worker         return std::move(parts[0]);
401*c8dee2aaSAndroid Build Coastguard Worker     }
402*c8dee2aaSAndroid Build Coastguard Worker 
403*c8dee2aaSAndroid Build Coastguard Worker     // Copy all of the markers and stitch them together.
404*c8dee2aaSAndroid Build Coastguard Worker     auto result = SkData::MakeUninitialized(partsTotalSize);
405*c8dee2aaSAndroid Build Coastguard Worker     void* copyDest = result->writable_data();
406*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& part : parts) {
407*c8dee2aaSAndroid Build Coastguard Worker         memcpy(copyDest, part->data(), part->size());
408*c8dee2aaSAndroid Build Coastguard Worker         copyDest = SkTAddOffset<void>(copyDest, part->size());
409*c8dee2aaSAndroid Build Coastguard Worker     }
410*c8dee2aaSAndroid Build Coastguard Worker     return result;
411*c8dee2aaSAndroid Build Coastguard Worker }
412*c8dee2aaSAndroid Build Coastguard Worker 
SkJpegMetadataDecoderImpl(SkJpegMarkerList markerList)413*c8dee2aaSAndroid Build Coastguard Worker SkJpegMetadataDecoderImpl::SkJpegMetadataDecoderImpl(SkJpegMarkerList markerList)
414*c8dee2aaSAndroid Build Coastguard Worker         : fMarkerList(std::move(markerList)) {}
415*c8dee2aaSAndroid Build Coastguard Worker 
SkJpegMetadataDecoderImpl(sk_sp<SkData> data)416*c8dee2aaSAndroid Build Coastguard Worker SkJpegMetadataDecoderImpl::SkJpegMetadataDecoderImpl(sk_sp<SkData> data) {
417*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
418*c8dee2aaSAndroid Build Coastguard Worker     SkJpegSegmentScanner scan(kJpegMarkerStartOfScan);
419*c8dee2aaSAndroid Build Coastguard Worker     scan.onBytes(data->data(), data->size());
420*c8dee2aaSAndroid Build Coastguard Worker     if (scan.hadError() || !scan.isDone()) {
421*c8dee2aaSAndroid Build Coastguard Worker         SkCodecPrintf("Failed to scan header of MP image.\n");
422*c8dee2aaSAndroid Build Coastguard Worker         return;
423*c8dee2aaSAndroid Build Coastguard Worker     }
424*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& segment : scan.getSegments()) {
425*c8dee2aaSAndroid Build Coastguard Worker         // Save the APP1 and APP2 parameters (which includes Exif, XMP, ICC, and MPF).
426*c8dee2aaSAndroid Build Coastguard Worker         if (segment.marker != kJpegMarkerAPP0 + 1 && segment.marker != kJpegMarkerAPP0 + 2) {
427*c8dee2aaSAndroid Build Coastguard Worker             continue;
428*c8dee2aaSAndroid Build Coastguard Worker         }
429*c8dee2aaSAndroid Build Coastguard Worker         auto parameters = SkJpegSegmentScanner::GetParameters(data.get(), segment);
430*c8dee2aaSAndroid Build Coastguard Worker         if (!parameters) {
431*c8dee2aaSAndroid Build Coastguard Worker             continue;
432*c8dee2aaSAndroid Build Coastguard Worker         }
433*c8dee2aaSAndroid Build Coastguard Worker         fMarkerList.emplace_back(segment.marker, std::move(parameters));
434*c8dee2aaSAndroid Build Coastguard Worker     }
435*c8dee2aaSAndroid Build Coastguard Worker #endif
436*c8dee2aaSAndroid Build Coastguard Worker }
437*c8dee2aaSAndroid Build Coastguard Worker 
getExifMetadata(bool copyData) const438*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkJpegMetadataDecoderImpl::getExifMetadata(bool copyData) const {
439*c8dee2aaSAndroid Build Coastguard Worker     return read_metadata(fMarkerList,
440*c8dee2aaSAndroid Build Coastguard Worker                          kExifMarker,
441*c8dee2aaSAndroid Build Coastguard Worker                          kExifSig,
442*c8dee2aaSAndroid Build Coastguard Worker                          sizeof(kExifSig),
443*c8dee2aaSAndroid Build Coastguard Worker                          /*signaturePadding=*/1,
444*c8dee2aaSAndroid Build Coastguard Worker                          /*bytesInIndex=*/0,
445*c8dee2aaSAndroid Build Coastguard Worker                          copyData);
446*c8dee2aaSAndroid Build Coastguard Worker }
447*c8dee2aaSAndroid Build Coastguard Worker 
getICCProfileData(bool copyData) const448*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkJpegMetadataDecoderImpl::getICCProfileData(bool copyData) const {
449*c8dee2aaSAndroid Build Coastguard Worker     return read_metadata(fMarkerList,
450*c8dee2aaSAndroid Build Coastguard Worker                          kICCMarker,
451*c8dee2aaSAndroid Build Coastguard Worker                          kICCSig,
452*c8dee2aaSAndroid Build Coastguard Worker                          sizeof(kICCSig),
453*c8dee2aaSAndroid Build Coastguard Worker                          /*signaturePadding=*/0,
454*c8dee2aaSAndroid Build Coastguard Worker                          kICCMarkerIndexSize,
455*c8dee2aaSAndroid Build Coastguard Worker                          copyData);
456*c8dee2aaSAndroid Build Coastguard Worker }
457*c8dee2aaSAndroid Build Coastguard Worker 
getISOGainmapMetadata(bool copyData) const458*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> SkJpegMetadataDecoderImpl::getISOGainmapMetadata(bool copyData) const {
459*c8dee2aaSAndroid Build Coastguard Worker     return read_metadata(fMarkerList,
460*c8dee2aaSAndroid Build Coastguard Worker                          kISOGainmapMarker,
461*c8dee2aaSAndroid Build Coastguard Worker                          kISOGainmapSig,
462*c8dee2aaSAndroid Build Coastguard Worker                          sizeof(kISOGainmapSig),
463*c8dee2aaSAndroid Build Coastguard Worker                          /*signaturePadding=*/0,
464*c8dee2aaSAndroid Build Coastguard Worker                          /*bytesInIndex=*/0,
465*c8dee2aaSAndroid Build Coastguard Worker                          copyData);
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker 
mightHaveGainmapImage() const468*c8dee2aaSAndroid Build Coastguard Worker bool SkJpegMetadataDecoderImpl::mightHaveGainmapImage() const {
469*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
470*c8dee2aaSAndroid Build Coastguard Worker     // All supported gainmap formats require MPF. Reject images that do not have MPF.
471*c8dee2aaSAndroid Build Coastguard Worker     return find_mp_params(fMarkerList, nullptr, nullptr) != nullptr;
472*c8dee2aaSAndroid Build Coastguard Worker #else
473*c8dee2aaSAndroid Build Coastguard Worker     return false;
474*c8dee2aaSAndroid Build Coastguard Worker #endif
475*c8dee2aaSAndroid Build Coastguard Worker }
476*c8dee2aaSAndroid Build Coastguard Worker 
findGainmapImage(sk_sp<SkData> baseImageData,sk_sp<SkData> & outGainmapImageData,SkGainmapInfo & outGainmapInfo)477*c8dee2aaSAndroid Build Coastguard Worker bool SkJpegMetadataDecoderImpl::findGainmapImage(sk_sp<SkData> baseImageData,
478*c8dee2aaSAndroid Build Coastguard Worker                                                  sk_sp<SkData>& outGainmapImageData,
479*c8dee2aaSAndroid Build Coastguard Worker                                                  SkGainmapInfo& outGainmapInfo) {
480*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_CODEC_DECODES_JPEG_GAINMAPS
481*c8dee2aaSAndroid Build Coastguard Worker     auto baseImageStream = SkMemoryStream::Make(baseImageData);
482*c8dee2aaSAndroid Build Coastguard Worker     auto sourceMgr = SkJpegSourceMgr::Make(baseImageStream.get());
483*c8dee2aaSAndroid Build Coastguard Worker     return findGainmapImage(sourceMgr.get(), outGainmapImageData, outGainmapInfo);
484*c8dee2aaSAndroid Build Coastguard Worker #else
485*c8dee2aaSAndroid Build Coastguard Worker     return false;
486*c8dee2aaSAndroid Build Coastguard Worker #endif
487*c8dee2aaSAndroid Build Coastguard Worker }
488*c8dee2aaSAndroid Build Coastguard Worker 
Make(std::vector<Segment> segments)489*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkJpegMetadataDecoder> SkJpegMetadataDecoder::Make(std::vector<Segment> segments) {
490*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<SkJpegMetadataDecoderImpl>(std::move(segments));
491*c8dee2aaSAndroid Build Coastguard Worker }
492*c8dee2aaSAndroid Build Coastguard Worker 
Make(sk_sp<SkData> data)493*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkJpegMetadataDecoder> SkJpegMetadataDecoder::Make(sk_sp<SkData> data) {
494*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<SkJpegMetadataDecoderImpl>(std::move(data));
495*c8dee2aaSAndroid Build Coastguard Worker }
496