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