Flutter Engine
The Flutter Engine
SkXmp.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
11#include "include/core/SkData.h"
17#include "src/xml/SkDOM.h"
18
19#include <cmath>
20#include <cstdint>
21#include <cstring>
22#include <string>
23#include <vector>
24#include <utility>
25
26////////////////////////////////////////////////////////////////////////////////////////////////////
27// XMP parsing helper functions
28
29const char* kXmlnsPrefix = "xmlns:";
30const size_t kXmlnsPrefixLength = 6;
31
32static const char* get_namespace_prefix(const char* name) {
33 if (strlen(name) <= kXmlnsPrefixLength) {
34 return nullptr;
35 }
36 return name + kXmlnsPrefixLength;
37}
38
39/*
40 * Given a node, see if that node has only one child with the indicated name. If so, see if that
41 * child has only a single child of its own, and that child is text. If all of that is the case
42 * then return the text, otherwise return nullptr.
43 *
44 * In the following example, innerText will be returned.
45 * <node><childName>innerText</childName></node>
46 *
47 * In the following examples, nullptr will be returned (because there are multiple children with
48 * childName in the first case, and because the child has children of its own in the second).
49 * <node><childName>innerTextA</childName><childName>innerTextB</childName></node>
50 * <node><childName>innerText<otherGrandChild/></childName></node>
51 */
52static const char* get_unique_child_text(const SkDOM& dom,
53 const SkDOM::Node* node,
54 const std::string& childName) {
55 // Fail if there are multiple children with childName.
56 if (dom.countChildren(node, childName.c_str()) != 1) {
57 return nullptr;
58 }
59 const auto* child = dom.getFirstChild(node, childName.c_str());
60 if (!child) {
61 return nullptr;
62 }
63 // Fail if the child has any children besides text.
64 if (dom.countChildren(child) != 1) {
65 return nullptr;
66 }
67 const auto* grandChild = dom.getFirstChild(child);
68 if (dom.getType(grandChild) != SkDOM::kText_Type) {
69 return nullptr;
70 }
71 // Return the text.
72 return dom.getName(grandChild);
73}
74
75/*
76 * Given a node, find a child node of the specified type.
77 *
78 * If there exists a child node with name |prefix| + ":" + |type|, then return that child.
79 *
80 * If there exists a child node with name "rdf:type" that has attribute "rdf:resource" with value
81 * of |type|, then if there also exists a child node with name "rdf:value" with attribute
82 * "rdf:parseType" of "Resource", then return that child node with name "rdf:value". See Example
83 * 3 in section 7.9.2.5: RDF Typed Nodes.
84 * TODO(ccameron): This should also accept a URI for the type.
85 */
86static const SkDOM::Node* get_typed_child(const SkDOM* dom,
87 const SkDOM::Node* node,
88 const std::string& prefix,
89 const std::string& type) {
90 const auto name = prefix + std::string(":") + type;
91 const SkDOM::Node* child = dom->getFirstChild(node, name.c_str());
92 if (child) {
93 return child;
94 }
95
96 const SkDOM::Node* typeChild = dom->getFirstChild(node, "rdf:type");
97 if (!typeChild) {
98 return nullptr;
99 }
100 const char* typeChildResource = dom->findAttr(typeChild, "rdf:resource");
101 if (!typeChildResource || typeChildResource != type) {
102 return nullptr;
103 }
104
105 const SkDOM::Node* valueChild = dom->getFirstChild(node, "rdf:value");
106 if (!valueChild) {
107 return nullptr;
108 }
109 const char* valueChildParseType = dom->findAttr(valueChild, "rdf:parseType");
110 if (!valueChildParseType || strcmp(valueChildParseType, "Resource") != 0) {
111 return nullptr;
112 }
113 return valueChild;
114}
115
116/*
117 * Given a node, return its value for the specified attribute.
118 *
119 * This will first look for an attribute with the name |prefix| + ":" + |key|, and return the value
120 * for that attribute.
121 *
122 * This will then look for a child node of name |prefix| + ":" + |key|, and return the field value
123 * for that child.
124 */
125static const char* get_attr(const SkDOM* dom,
126 const SkDOM::Node* node,
127 const std::string& prefix,
128 const std::string& key) {
129 const auto name = prefix + ":" + key;
130 const char* attr = dom->findAttr(node, name.c_str());
131 if (attr) {
132 return attr;
133 }
134 return get_unique_child_text(*dom, node, name);
135}
136
137// Perform get_attr and parse the result as a bool.
138static bool get_attr_bool(const SkDOM* dom,
139 const SkDOM::Node* node,
140 const std::string& prefix,
141 const std::string& key,
142 bool* outValue) {
143 const char* attr = get_attr(dom, node, prefix, key);
144 if (!attr) {
145 return false;
146 }
147 switch (SkParse::FindList(attr, "False,True")) {
148 case 0:
149 *outValue = false;
150 return true;
151 case 1:
152 *outValue = true;
153 return true;
154 default:
155 break;
156 }
157 return false;
158}
159
160// Perform get_attr and parse the result as an int32_t.
161static bool get_attr_int32(const SkDOM* dom,
162 const SkDOM::Node* node,
163 const std::string& prefix,
164 const std::string& key,
165 int32_t* value) {
166 const char* attr = get_attr(dom, node, prefix, key);
167 if (!attr) {
168 return false;
169 }
170 if (!SkParse::FindS32(attr, value)) {
171 return false;
172 }
173 return true;
174}
175
176// Perform get_attr and parse the result as a float.
177static bool get_attr_float(const SkDOM* dom,
178 const SkDOM::Node* node,
179 const std::string& prefix,
180 const std::string& key,
181 float* outValue) {
182 const char* attr = get_attr(dom, node, prefix, key);
183 if (!attr) {
184 return false;
185 }
186 SkScalar value = 0.f;
187 if (SkParse::FindScalar(attr, &value)) {
188 *outValue = value;
189 return true;
190 }
191 return false;
192}
193
194// Perform get_attr and parse the result as three comma-separated floats. Return the result as an
195// SkColor4f with the alpha component set to 1.
197 const SkDOM::Node* node,
198 const std::string& prefix,
199 const std::string& key,
200 SkColor4f* outValue) {
201 const auto name = prefix + ":" + key;
202
203 // Fail if there are multiple children with childName.
204 if (dom->countChildren(node, name.c_str()) != 1) {
205 return false;
206 }
207 // Find the child.
208 const auto* child = dom->getFirstChild(node, name.c_str());
209 if (!child) {
210 return false;
211 }
212
213 // Search for the rdf:Seq child.
214 const auto* seq = dom->getFirstChild(child, "rdf:Seq");
215 if (!seq) {
216 return false;
217 }
218
219 size_t count = 0;
220 SkScalar values[3] = {0.f, 0.f, 0.f};
221 for (const auto* liNode = dom->getFirstChild(seq, "rdf:li"); liNode;
222 liNode = dom->getNextSibling(liNode, "rdf:li")) {
223 if (count > 2) {
224 SkCodecPrintf("Too many items in list.\n");
225 return false;
226 }
227 if (dom->countChildren(liNode) != 1) {
228 SkCodecPrintf("Item can only have one child.\n");
229 return false;
230 }
231 const auto* liTextNode = dom->getFirstChild(liNode);
232 if (dom->getType(liTextNode) != SkDOM::kText_Type) {
233 SkCodecPrintf("Item's only child must be text.\n");
234 return false;
235 }
236 const char* liText = dom->getName(liTextNode);
237 if (!liText) {
238 SkCodecPrintf("Failed to get item's text.\n");
239 return false;
240 }
241 if (!SkParse::FindScalar(liText, values + count)) {
242 SkCodecPrintf("Failed to parse item's text to float.\n");
243 return false;
244 }
245 count += 1;
246 }
247 if (count < 3) {
248 SkCodecPrintf("List didn't have enough items.\n");
249 return false;
250 }
251 *outValue = {values[0], values[1], values[2], 1.f};
252 return true;
253}
254
255static bool get_attr_float3(const SkDOM* dom,
256 const SkDOM::Node* node,
257 const std::string& prefix,
258 const std::string& key,
259 SkColor4f* outValue) {
260 if (get_attr_float3_as_list(dom, node, prefix, key, outValue)) {
261 return true;
262 }
263 SkScalar value = -1.0;
264 if (get_attr_float(dom, node, prefix, key, &value)) {
265 *outValue = {value, value, value, 1.f};
266 return true;
267 }
268 return false;
269}
270
271static void find_uri_namespaces(const SkDOM& dom,
272 const SkDOM::Node* node,
273 size_t count,
274 const char* uris[],
275 const char* outNamespaces[]) {
276 // Search all attributes for xmlns:NAMESPACEi="URIi".
277 for (const auto* attr = dom.getFirstAttr(node); attr; attr = dom.getNextAttr(node, attr)) {
278 const char* attrName = dom.getAttrName(node, attr);
279 const char* attrValue = dom.getAttrValue(node, attr);
280 if (!attrName || !attrValue) {
281 continue;
282 }
283 // Make sure the name starts with "xmlns:".
284 if (strlen(attrName) <= kXmlnsPrefixLength) {
285 continue;
286 }
287 if (memcmp(attrName, kXmlnsPrefix, kXmlnsPrefixLength) != 0) {
288 continue;
289 }
290 // Search for a requested URI that matches.
291 for (size_t i = 0; i < count; ++i) {
292 if (strcmp(attrValue, uris[i]) != 0) {
293 continue;
294 }
295 outNamespaces[i] = attrName;
296 }
297 }
298}
299
300// See SkXmp::findUriNamespaces. This function has the same behavior, but only searches
301// a single SkDOM.
303 size_t count,
304 const char* uris[],
305 const char* outNamespaces[]) {
306 const SkDOM::Node* root = dom.getRootNode();
307 if (!root) {
308 return nullptr;
309 }
310
311 // Ensure that the root node identifies itself as XMP metadata.
312 const char* rootName = dom.getName(root);
313 if (!rootName || strcmp(rootName, "x:xmpmeta") != 0) {
314 return nullptr;
315 }
316
317 // Iterate the children with name rdf:RDF.
318 const char* kRdf = "rdf:RDF";
319 for (const auto* rdf = dom.getFirstChild(root, kRdf); rdf;
320 rdf = dom.getNextSibling(rdf, kRdf)) {
321 std::vector<const char*> rdfNamespaces(count, nullptr);
322 find_uri_namespaces(dom, rdf, count, uris, rdfNamespaces.data());
323
324 // Iterate the children with name rdf::Description.
325 const char* kDesc = "rdf:Description";
326 for (const auto* desc = dom.getFirstChild(rdf, kDesc); desc;
327 desc = dom.getNextSibling(desc, kDesc)) {
328 std::vector<const char*> descNamespaces = rdfNamespaces;
329 find_uri_namespaces(dom, desc, count, uris, descNamespaces.data());
330
331 // If we have a match for all the requested URIs, return.
332 bool foundAllUris = true;
333 for (size_t i = 0; i < count; ++i) {
334 if (!descNamespaces[i]) {
335 foundAllUris = false;
336 break;
337 }
338 }
339 if (foundAllUris) {
340 for (size_t i = 0; i < count; ++i) {
341 outNamespaces[i] = descNamespaces[i];
342 }
343 return desc;
344 }
345 }
346 }
347 return nullptr;
348}
349
350////////////////////////////////////////////////////////////////////////////////////////////////////
351// SkXmpImpl
352
353class SkXmpImpl final : public SkXmp {
354public:
355 SkXmpImpl() = default;
356
357 bool getGainmapInfoAdobe(SkGainmapInfo* info) const override;
358 bool getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo* info) const override;
359 bool getContainerGainmapLocation(size_t* offset, size_t* size) const override;
360 const char* getExtendedXmpGuid() const override;
361 // Parse the given xmp data and store it into either the standard (main) DOM or the extended
362 // DOM. Returns true on successful parsing.
363 bool parseDom(sk_sp<SkData> xmpData, bool extended);
364
365private:
366 bool findUriNamespaces(size_t count,
367 const char* uris[],
368 const char* outNamespaces[],
369 const SkDOM** outDom,
370 const SkDOM::Node** outNode) const;
371
372 SkDOM fStandardDOM;
373 SkDOM fExtendedDOM;
374};
375
376const char* SkXmpImpl::getExtendedXmpGuid() const {
377 const char* namespaces[1] = {nullptr};
378 const char* uris[1] = {"http://ns.adobe.com/xmp/note/"};
379 const auto* extendedNode = find_uri_namespaces(fStandardDOM, 1, uris, namespaces);
380 if (!extendedNode) {
381 return nullptr;
382 }
383 const auto xmpNotePrefix = get_namespace_prefix(namespaces[0]);
384 // Extract the GUID (the MD5 hash) of the extended metadata.
385 return get_attr(&fStandardDOM, extendedNode, xmpNotePrefix, "HasExtendedXMP");
386}
387
388bool SkXmpImpl::findUriNamespaces(size_t count,
389 const char* uris[],
390 const char* outNamespaces[],
391 const SkDOM** outDom,
392 const SkDOM::Node** outNode) const {
393 // See XMP Specification Part 3: Storage in files, Section 1.1.3.1: Extended XMP in JPEG:
394 // A JPEG reader must recompose the StandardXMP and ExtendedXMP into a single data model tree
395 // containing all of the XMP for the JPEG file, and remove the xmpNote:HasExtendedXMP property.
396 // This code does not do that. Instead, it maintains the two separate trees and searches them
397 // sequentially.
398 *outNode = find_uri_namespaces(fStandardDOM, count, uris, outNamespaces);
399 if (*outNode) {
400 *outDom = &fStandardDOM;
401 return true;
402 }
403 *outNode = find_uri_namespaces(fExtendedDOM, count, uris, outNamespaces);
404 if (*outNode) {
405 *outDom = &fExtendedDOM;
406 return true;
407 }
408 *outDom = nullptr;
409 return false;
410}
411
412bool SkXmpImpl::getContainerGainmapLocation(size_t* outOffset, size_t* outSize) const {
413 // Find a node that matches the requested namespaces and URIs.
414 const char* namespaces[2] = {nullptr, nullptr};
415 const char* uris[2] = {"http://ns.google.com/photos/1.0/container/",
416 "http://ns.google.com/photos/1.0/container/item/"};
417 const SkDOM* dom = nullptr;
418 const SkDOM::Node* node = nullptr;
419 if (!findUriNamespaces(2, uris, namespaces, &dom, &node)) {
420 return false;
421 }
422 const char* containerPrefix = get_namespace_prefix(namespaces[0]);
423 const char* itemPrefix = get_namespace_prefix(namespaces[1]);
424
425 // The node must have a Container:Directory child.
426 const auto* directory = get_typed_child(dom, node, containerPrefix, "Directory");
427 if (!directory) {
428 SkCodecPrintf("Missing Container Directory");
429 return false;
430 }
431
432 // That Container:Directory must have a sequence of items.
433 const auto* seq = dom->getFirstChild(directory, "rdf:Seq");
434 if (!seq) {
435 SkCodecPrintf("Missing rdf:Seq");
436 return false;
437 }
438
439 // Iterate through the items in the Container:Directory's sequence. Keep a running sum of the
440 // Item:Length of all items that appear before the GainMap.
441 bool isFirstItem = true;
442 size_t offset = 0;
443 for (const auto* li = dom->getFirstChild(seq, "rdf:li"); li;
444 li = dom->getNextSibling(li, "rdf:li")) {
445 // Each list item must contain a Container:Item.
446 const auto* item = get_typed_child(dom, li, containerPrefix, "Item");
447 if (!item) {
448 SkCodecPrintf("List item does not have container Item.\n");
449 return false;
450 }
451 // A Semantic is required for every item.
452 const char* itemSemantic = get_attr(dom, item, itemPrefix, "Semantic");
453 if (!itemSemantic) {
454 SkCodecPrintf("Item is missing Semantic.\n");
455 return false;
456 }
457 // A Mime is required for every item.
458 const char* itemMime = get_attr(dom, item, itemPrefix, "Mime");
459 if (!itemMime) {
460 SkCodecPrintf("Item is missing Mime.\n");
461 return false;
462 }
463
464 if (isFirstItem) {
465 isFirstItem = false;
466 // The first item must be Primary.
467 if (strcmp(itemSemantic, "Primary") != 0) {
468 SkCodecPrintf("First item is not Primary.\n");
469 return false;
470 }
471 // The first item has mime type image/jpeg (we are decoding a jpeg).
472 if (strcmp(itemMime, "image/jpeg") != 0) {
473 SkCodecPrintf("Primary does not report that it is image/jpeg.\n");
474 return false;
475 }
476 // The first media item can contain a Padding attribute, which specifies additional
477 // padding between the end of the encoded primary image and the beginning of the next
478 // media item. Only the first media item can contain a Padding attribute.
479 int32_t padding = 0;
480 if (get_attr_int32(dom, item, itemPrefix, "Padding", &padding)) {
481 if (padding < 0) {
482 SkCodecPrintf("Item padding must be non-negative.");
483 return false;
484 }
485 offset += padding;
486 }
487 } else {
488 // A Length is required for all non-Primary items.
489 int32_t length = 0;
490 if (!get_attr_int32(dom, item, itemPrefix, "Length", &length)) {
491 SkCodecPrintf("Item length is absent.");
492 return false;
493 }
494 if (length < 0) {
495 SkCodecPrintf("Item length must be non-negative.");
496 return false;
497 }
498 // If this is not the recovery map, then read past it.
499 if (strcmp(itemSemantic, "GainMap") != 0) {
500 offset += length;
501 continue;
502 }
503 // The recovery map must have mime type image/jpeg in this implementation.
504 if (strcmp(itemMime, "image/jpeg") != 0) {
505 SkCodecPrintf("GainMap does not report that it is image/jpeg.\n");
506 return false;
507 }
508
509 // Populate the location in the file at which to find the gainmap image.
510 *outOffset = offset;
511 *outSize = length;
512 return true;
513 }
514 }
515 return false;
516}
517
518// Return true if the specified XMP metadata identifies this image as an HDR gainmap.
519bool SkXmpImpl::getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo* info) const {
520 // Find a node that matches the requested namespaces and URIs.
521 const char* namespaces[2] = {nullptr, nullptr};
522 const char* uris[2] = {"http://ns.apple.com/pixeldatainfo/1.0/",
523 "http://ns.apple.com/HDRGainMap/1.0/"};
524 const SkDOM* dom = nullptr;
525 const SkDOM::Node* node = nullptr;
526 if (!findUriNamespaces(2, uris, namespaces, &dom, &node)) {
527 return false;
528 }
529 const char* adpiPrefix = get_namespace_prefix(namespaces[0]);
530 const char* hdrGainMapPrefix = get_namespace_prefix(namespaces[1]);
531
532 const char* auxiliaryImageType = get_attr(dom, node, adpiPrefix, "AuxiliaryImageType");
533 if (!auxiliaryImageType) {
534 SkCodecPrintf("Did not find AuxiliaryImageType.\n");
535 return false;
536 }
537 if (strcmp(auxiliaryImageType, "urn:com:apple:photo:2020:aux:hdrgainmap") != 0) {
538 SkCodecPrintf("AuxiliaryImageType was not HDR gain map.\n");
539 return false;
540 }
541
542 // Require that the gainmap version be present, but do not require a specific version.
543 int32_t version = 0;
544 if (!get_attr_int32(dom, node, hdrGainMapPrefix, "HDRGainMapVersion", &version)) {
545 SkCodecPrintf("Did not find HDRGainMapVersion.\n");
546 return false;
547 }
548
549 // If the XMP also specifies a HDRGainMapHeadroom parameter, then prefer that parameter to the
550 // parameter specified in the base image Exif.
551 float hdrHeadroom = exifHdrHeadroom;
552 float xmpHdrHeadroom = 0.f;
553 if (get_attr_float(dom, node, hdrGainMapPrefix, "HDRGainMapHeadroom", &xmpHdrHeadroom)) {
554 hdrHeadroom = xmpHdrHeadroom;
555 }
556
557 // This node will often have StoredFormat and NativeFormat children that have inner text that
558 // specifies the integer 'L008' (also known as kCVPixelFormatType_OneComponent8).
559 info->fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f};
560 info->fGainmapRatioMax = {hdrHeadroom, hdrHeadroom, hdrHeadroom, 1.f};
561 info->fGainmapGamma = {1.f, 1.f, 1.f, 1.f};
562 info->fEpsilonSdr = {0.f, 0.f, 0.f, 1.f};
563 info->fEpsilonHdr = {0.f, 0.f, 0.f, 1.f};
564 info->fDisplayRatioSdr = 1.f;
565 info->fDisplayRatioHdr = hdrHeadroom;
568 return true;
569}
570
572 // Find a node that matches the requested namespace and URI.
573 const char* namespaces[1] = {nullptr};
574 const char* uris[1] = {"http://ns.adobe.com/hdr-gain-map/1.0/"};
575 const SkDOM* dom = nullptr;
576 const SkDOM::Node* node = nullptr;
577 if (!findUriNamespaces(1, uris, namespaces, &dom, &node)) {
578 return false;
579 }
580 const char* hdrgmPrefix = get_namespace_prefix(namespaces[0]);
581
582 // Require that hdrgm:Version="1.0" be present.
583 const char* version = get_attr(dom, node, hdrgmPrefix, "Version");
584 if (!version) {
585 SkCodecPrintf("Version attribute is absent.\n");
586 return false;
587 }
588 if (strcmp(version, "1.0") != 0) {
589 SkCodecPrintf("Version is \"%s\", not \"1.0\".\n", version);
590 return false;
591 }
592
593 // Initialize the parameters to their defaults.
594 bool baseRenditionIsHDR = false;
595 SkColor4f gainMapMin = {0.f, 0.f, 0.f, 1.f}; // log2 value
596 SkColor4f gainMapMax = {1.f, 1.f, 1.f, 1.f}; // log2 value
597 SkColor4f gamma = {1.f, 1.f, 1.f, 1.f};
598 SkColor4f offsetSdr = {1.f / 64.f, 1.f / 64.f, 1.f / 64.f, 0.f};
599 SkColor4f offsetHdr = {1.f / 64.f, 1.f / 64.f, 1.f / 64.f, 0.f};
600 SkScalar hdrCapacityMin = 0.f; // log2 value
601 SkScalar hdrCapacityMax = 1.f; // log2 value
602
603 // Read all parameters that are present.
604 get_attr_bool(dom, node, hdrgmPrefix, "BaseRenditionIsHDR", &baseRenditionIsHDR);
605 get_attr_float3(dom, node, hdrgmPrefix, "GainMapMin", &gainMapMin);
606 get_attr_float3(dom, node, hdrgmPrefix, "GainMapMax", &gainMapMax);
607 get_attr_float3(dom, node, hdrgmPrefix, "Gamma", &gamma);
608 get_attr_float3(dom, node, hdrgmPrefix, "OffsetSDR", &offsetSdr);
609 get_attr_float3(dom, node, hdrgmPrefix, "OffsetHDR", &offsetHdr);
610 get_attr_float(dom, node, hdrgmPrefix, "HDRCapacityMin", &hdrCapacityMin);
611 get_attr_float(dom, node, hdrgmPrefix, "HDRCapacityMax", &hdrCapacityMax);
612
613 // Translate all parameters to SkGainmapInfo's expected format.
614 if (!outGainmapInfo) {
615 return true;
616 }
617 const float kLog2 = std::log(2.f);
618 outGainmapInfo->fGainmapRatioMin = {std::exp(gainMapMin.fR * kLog2),
619 std::exp(gainMapMin.fG * kLog2),
620 std::exp(gainMapMin.fB * kLog2),
621 1.f};
622 outGainmapInfo->fGainmapRatioMax = {std::exp(gainMapMax.fR * kLog2),
623 std::exp(gainMapMax.fG * kLog2),
624 std::exp(gainMapMax.fB * kLog2),
625 1.f};
626 outGainmapInfo->fGainmapGamma = {1.f / gamma.fR, 1.f / gamma.fG, 1.f / gamma.fB, 1.f};
627 outGainmapInfo->fEpsilonSdr = offsetSdr;
628 outGainmapInfo->fEpsilonHdr = offsetHdr;
629 outGainmapInfo->fDisplayRatioSdr = std::exp(hdrCapacityMin * kLog2);
630 outGainmapInfo->fDisplayRatioHdr = std::exp(hdrCapacityMax * kLog2);
631 if (baseRenditionIsHDR) {
633 } else {
635 }
636 return true;
637}
638
640 SkDOM* dom = extended ? &fExtendedDOM : &fStandardDOM;
641 auto xmpdStream = SkMemoryStream::Make(std::move(xmpData));
642 if (!dom->build(*xmpdStream)) {
643 SkCodecPrintf("Failed to parse XMP %s metadata.\n", extended ? "extended" : "standard");
644 return false;
645 }
646 return true;
647}
648
649////////////////////////////////////////////////////////////////////////////////////////////////////
650// SkXmp
651
652std::unique_ptr<SkXmp> SkXmp::Make(sk_sp<SkData> xmpData) {
653 std::unique_ptr<SkXmpImpl> xmp(new SkXmpImpl);
654 if (!xmp->parseDom(std::move(xmpData), /*extended=*/false)) {
655 return nullptr;
656 }
657 return xmp;
658}
659
660std::unique_ptr<SkXmp> SkXmp::Make(sk_sp<SkData> xmpStandard, sk_sp<SkData> xmpExtended) {
661 std::unique_ptr<SkXmpImpl> xmp(new SkXmpImpl);
662 if (!xmp->parseDom(std::move(xmpStandard), /*extended=*/false)) {
663 return nullptr;
664 }
665 // Try to parse extended xmp but ignore the return value: if parsing fails, we'll still return
666 // the standard xmp.
667 (void)xmp->parseDom(std::move(xmpExtended), /*extended=*/true);
668 return xmp;
669}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
int count
Definition: FontMgrTest.cpp:50
#define SkCodecPrintf(...)
Definition: SkCodecPriv.h:23
static void find_uri_namespaces(const SkDOM &dom, const SkDOM::Node *node, size_t count, const char *uris[], const char *outNamespaces[])
Definition: SkXmp.cpp:271
static bool get_attr_float3_as_list(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key, SkColor4f *outValue)
Definition: SkXmp.cpp:196
const size_t kXmlnsPrefixLength
Definition: SkXmp.cpp:30
static const char * get_unique_child_text(const SkDOM &dom, const SkDOM::Node *node, const std::string &childName)
Definition: SkXmp.cpp:52
static const char * get_attr(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key)
Definition: SkXmp.cpp:125
static bool get_attr_bool(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key, bool *outValue)
Definition: SkXmp.cpp:138
static bool get_attr_float3(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key, SkColor4f *outValue)
Definition: SkXmp.cpp:255
static const char * get_namespace_prefix(const char *name)
Definition: SkXmp.cpp:32
static bool get_attr_int32(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key, int32_t *value)
Definition: SkXmp.cpp:161
const char * kXmlnsPrefix
Definition: SkXmp.cpp:29
static const SkDOM::Node * get_typed_child(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &type)
Definition: SkXmp.cpp:86
static bool get_attr_float(const SkDOM *dom, const SkDOM::Node *node, const std::string &prefix, const std::string &key, float *outValue)
Definition: SkXmp.cpp:177
GLenum type
Definition: SkDOM.h:24
@ kText_Type
Definition: SkDOM.h:44
static std::unique_ptr< SkMemoryStream > Make(sk_sp< SkData > data)
Definition: SkStream.cpp:314
static const char * FindScalar(const char str[], SkScalar *value)
Definition: SkParse.cpp:216
static const char * FindS32(const char str[], int32_t *value)
Definition: SkParse.cpp:143
static int FindList(const char str[], const char list[])
Definition: SkParse.cpp:278
const char * getExtendedXmpGuid() const override
Definition: SkXmp.cpp:376
bool parseDom(sk_sp< SkData > xmpData, bool extended)
Definition: SkXmp.cpp:639
bool getGainmapInfoApple(float exifHdrHeadroom, SkGainmapInfo *info) const override
Definition: SkXmp.cpp:519
SkXmpImpl()=default
bool getContainerGainmapLocation(size_t *offset, size_t *size) const override
Definition: SkXmp.cpp:412
bool getGainmapInfoAdobe(SkGainmapInfo *info) const override
Definition: SkXmp.cpp:571
Definition: SkXmp.h:23
static std::unique_ptr< SkXmp > Make(sk_sp< SkData > xmpData)
Definition: SkXmp.cpp:652
float SkScalar
Definition: extension.cpp:12
uint8_t value
size_t length
Definition: dom.py:1
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
string root
Definition: scale_cpu.py:20
SeparatedVector2 offset
SkColor4f fGainmapRatioMax
Definition: SkGainmapInfo.h:49
SkColor4f fEpsilonSdr
Definition: SkGainmapInfo.h:55
SkColor4f fGainmapGamma
Definition: SkGainmapInfo.h:50
SkColor4f fGainmapRatioMin
Definition: SkGainmapInfo.h:48
BaseImageType fBaseImageType
Definition: SkGainmapInfo.h:75
float fDisplayRatioSdr
Definition: SkGainmapInfo.h:65
SkColor4f fEpsilonHdr
Definition: SkGainmapInfo.h:56
float fDisplayRatioHdr
Definition: SkGainmapInfo.h:66