/src/libultrahdr/lib/src/jpegrutils.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2022 The Android Open Source Project |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <algorithm> |
18 | | #include <cmath> |
19 | | |
20 | | #include "ultrahdr/ultrahdrcommon.h" |
21 | | #include "ultrahdr/jpegr.h" |
22 | | #include "ultrahdr/jpegrutils.h" |
23 | | |
24 | | #include "image_io/xml/xml_reader.h" |
25 | | #include "image_io/xml/xml_writer.h" |
26 | | #include "image_io/base/message_handler.h" |
27 | | #include "image_io/xml/xml_element_rules.h" |
28 | | #include "image_io/xml/xml_handler.h" |
29 | | #include "image_io/xml/xml_rule.h" |
30 | | |
31 | | using namespace photos_editing_formats::image_io; |
32 | | using namespace std; |
33 | | |
34 | | namespace ultrahdr { |
35 | | /* |
36 | | * Helper function used for generating XMP metadata. |
37 | | * |
38 | | * @param prefix The prefix part of the name. |
39 | | * @param suffix The suffix part of the name. |
40 | | * @return A name of the form "prefix:suffix". |
41 | | */ |
42 | 28 | static inline string Name(const string& prefix, const string& suffix) { |
43 | 28 | std::stringstream ss; |
44 | 28 | ss << prefix << ":" << suffix; |
45 | 28 | return ss.str(); |
46 | 28 | } |
47 | | |
48 | 0 | DataStruct::DataStruct(size_t s) { |
49 | 0 | data = malloc(s); |
50 | 0 | length = s; |
51 | 0 | memset(data, 0, s); |
52 | 0 | writePos = 0; |
53 | 0 | } |
54 | | |
55 | 0 | DataStruct::~DataStruct() { |
56 | 0 | if (data != nullptr) { |
57 | 0 | free(data); |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | 0 | void* DataStruct::getData() { return data; } |
62 | | |
63 | 0 | size_t DataStruct::getLength() { return length; } |
64 | | |
65 | 0 | size_t DataStruct::getBytesWritten() { return writePos; } |
66 | | |
67 | 0 | bool DataStruct::write8(uint8_t value) { |
68 | 0 | uint8_t v = value; |
69 | 0 | return write(&v, 1); |
70 | 0 | } |
71 | | |
72 | 0 | bool DataStruct::write16(uint16_t value) { |
73 | 0 | uint16_t v = value; |
74 | 0 | return write(&v, 2); |
75 | 0 | } |
76 | | |
77 | 0 | bool DataStruct::write32(uint32_t value) { |
78 | 0 | uint32_t v = value; |
79 | 0 | return write(&v, 4); |
80 | 0 | } |
81 | | |
82 | 0 | bool DataStruct::write(const void* src, size_t size) { |
83 | 0 | if (writePos + size > length) { |
84 | 0 | ALOGE("Writing out of boundary: write position: %zd, size: %zd, capacity: %zd", writePos, size, |
85 | 0 | length); |
86 | 0 | return false; |
87 | 0 | } |
88 | 0 | memcpy((uint8_t*)data + writePos, src, size); |
89 | 0 | writePos += size; |
90 | 0 | return true; |
91 | 0 | } |
92 | | |
93 | | /* |
94 | | * Helper function used for writing data to destination. |
95 | | */ |
96 | | uhdr_error_info_t Write(uhdr_compressed_image_t* destination, const void* source, size_t length, |
97 | 0 | size_t& position) { |
98 | 0 | if (position + length > destination->capacity) { |
99 | 0 | uhdr_error_info_t status; |
100 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
101 | 0 | status.has_detail = 1; |
102 | 0 | snprintf(status.detail, sizeof status.detail, |
103 | 0 | "output buffer to store compressed data is too small: write position: %zd, size: %zd, " |
104 | 0 | "capacity: %zd", |
105 | 0 | position, length, destination->capacity); |
106 | 0 | return status; |
107 | 0 | } |
108 | | |
109 | 0 | memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length); |
110 | 0 | position += length; |
111 | 0 | return g_no_error; |
112 | 0 | } |
113 | | |
114 | | // Extremely simple XML Handler - just searches for interesting elements |
115 | | class XMPXmlHandler : public XmlHandler { |
116 | | public: |
117 | 2.77k | XMPXmlHandler() : XmlHandler() { |
118 | 2.77k | state = NotStrarted; |
119 | 2.77k | versionFound = false; |
120 | 2.77k | minContentBoostFound = false; |
121 | 2.77k | maxContentBoostFound = false; |
122 | 2.77k | gammaFound = false; |
123 | 2.77k | offsetSdrFound = false; |
124 | 2.77k | offsetHdrFound = false; |
125 | 2.77k | hdrCapacityMinFound = false; |
126 | 2.77k | hdrCapacityMaxFound = false; |
127 | 2.77k | baseRenditionIsHdrFound = false; |
128 | 2.77k | } |
129 | | |
130 | | enum ParseState { NotStrarted, Started, Done }; |
131 | | |
132 | 44.9k | virtual DataMatchResult StartElement(const XmlTokenContext& context) { |
133 | 44.9k | string val; |
134 | 44.9k | if (context.BuildTokenValue(&val)) { |
135 | 44.9k | if (!val.compare(containerName)) { |
136 | 5.12k | state = Started; |
137 | 39.7k | } else { |
138 | 39.7k | if (state != Done) { |
139 | 34.9k | state = NotStrarted; |
140 | 34.9k | } |
141 | 39.7k | } |
142 | 44.9k | } |
143 | 44.9k | return context.GetResult(); |
144 | 44.9k | } |
145 | | |
146 | 8.93k | virtual DataMatchResult FinishElement(const XmlTokenContext& context) { |
147 | 8.93k | if (state == Started) { |
148 | 1.79k | state = Done; |
149 | 1.79k | lastAttributeName = ""; |
150 | 1.79k | } |
151 | 8.93k | return context.GetResult(); |
152 | 8.93k | } |
153 | | |
154 | 44.9k | virtual DataMatchResult AttributeName(const XmlTokenContext& context) { |
155 | 44.9k | string val; |
156 | 44.9k | if (state == Started) { |
157 | 43.6k | if (context.BuildTokenValue(&val)) { |
158 | 43.6k | if (!val.compare(versionAttrName)) { |
159 | 442 | lastAttributeName = versionAttrName; |
160 | 43.2k | } else if (!val.compare(maxContentBoostAttrName)) { |
161 | 2.91k | lastAttributeName = maxContentBoostAttrName; |
162 | 40.3k | } else if (!val.compare(minContentBoostAttrName)) { |
163 | 144 | lastAttributeName = minContentBoostAttrName; |
164 | 40.1k | } else if (!val.compare(gammaAttrName)) { |
165 | 222 | lastAttributeName = gammaAttrName; |
166 | 39.9k | } else if (!val.compare(offsetSdrAttrName)) { |
167 | 570 | lastAttributeName = offsetSdrAttrName; |
168 | 39.3k | } else if (!val.compare(offsetHdrAttrName)) { |
169 | 34 | lastAttributeName = offsetHdrAttrName; |
170 | 39.3k | } else if (!val.compare(hdrCapacityMinAttrName)) { |
171 | 330 | lastAttributeName = hdrCapacityMinAttrName; |
172 | 39.0k | } else if (!val.compare(hdrCapacityMaxAttrName)) { |
173 | 42 | lastAttributeName = hdrCapacityMaxAttrName; |
174 | 38.9k | } else if (!val.compare(baseRenditionIsHdrAttrName)) { |
175 | 458 | lastAttributeName = baseRenditionIsHdrAttrName; |
176 | 38.5k | } else { |
177 | 38.5k | lastAttributeName = ""; |
178 | 38.5k | } |
179 | 43.6k | } |
180 | 43.6k | } |
181 | 44.9k | return context.GetResult(); |
182 | 44.9k | } |
183 | | |
184 | 44.2k | virtual DataMatchResult AttributeValue(const XmlTokenContext& context) { |
185 | 44.2k | string val; |
186 | 44.2k | if (state == Started) { |
187 | 43.1k | if (context.BuildTokenValue(&val, true)) { |
188 | 43.1k | if (!lastAttributeName.compare(versionAttrName)) { |
189 | 438 | versionStr = val; |
190 | 438 | versionFound = true; |
191 | 42.7k | } else if (!lastAttributeName.compare(maxContentBoostAttrName)) { |
192 | 2.90k | maxContentBoostStr = val; |
193 | 2.90k | maxContentBoostFound = true; |
194 | 39.7k | } else if (!lastAttributeName.compare(minContentBoostAttrName)) { |
195 | 142 | minContentBoostStr = val; |
196 | 142 | minContentBoostFound = true; |
197 | 39.6k | } else if (!lastAttributeName.compare(gammaAttrName)) { |
198 | 220 | gammaStr = val; |
199 | 220 | gammaFound = true; |
200 | 39.4k | } else if (!lastAttributeName.compare(offsetSdrAttrName)) { |
201 | 566 | offsetSdrStr = val; |
202 | 566 | offsetSdrFound = true; |
203 | 38.8k | } else if (!lastAttributeName.compare(offsetHdrAttrName)) { |
204 | 32 | offsetHdrStr = val; |
205 | 32 | offsetHdrFound = true; |
206 | 38.8k | } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) { |
207 | 328 | hdrCapacityMinStr = val; |
208 | 328 | hdrCapacityMinFound = true; |
209 | 38.5k | } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) { |
210 | 40 | hdrCapacityMaxStr = val; |
211 | 40 | hdrCapacityMaxFound = true; |
212 | 38.4k | } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) { |
213 | 456 | baseRenditionIsHdrStr = val; |
214 | 456 | baseRenditionIsHdrFound = true; |
215 | 456 | } |
216 | 43.1k | } |
217 | 43.1k | } |
218 | 44.2k | return context.GetResult(); |
219 | 44.2k | } |
220 | | |
221 | 36 | bool getVersion(string* version, bool* present) { |
222 | 36 | if (state == Done) { |
223 | 8 | *version = versionStr; |
224 | 8 | *present = versionFound; |
225 | 8 | return true; |
226 | 28 | } else { |
227 | 28 | return false; |
228 | 28 | } |
229 | 36 | } |
230 | | |
231 | 6 | bool getMaxContentBoost(float* max_content_boost, bool* present) { |
232 | 6 | if (state == Done) { |
233 | 6 | *present = maxContentBoostFound; |
234 | 6 | stringstream ss(maxContentBoostStr); |
235 | 6 | float val; |
236 | 6 | if (ss >> val) { |
237 | 4 | *max_content_boost = exp2(val); |
238 | 4 | return true; |
239 | 4 | } else { |
240 | 2 | return false; |
241 | 2 | } |
242 | 6 | } else { |
243 | 0 | return false; |
244 | 0 | } |
245 | 6 | } |
246 | | |
247 | 0 | bool getMinContentBoost(float* min_content_boost, bool* present) { |
248 | 0 | if (state == Done) { |
249 | 0 | *present = minContentBoostFound; |
250 | 0 | stringstream ss(minContentBoostStr); |
251 | 0 | float val; |
252 | 0 | if (ss >> val) { |
253 | 0 | *min_content_boost = exp2(val); |
254 | 0 | return true; |
255 | 0 | } else { |
256 | 0 | return false; |
257 | 0 | } |
258 | 0 | } else { |
259 | 0 | return false; |
260 | 0 | } |
261 | 0 | } |
262 | | |
263 | 0 | bool getGamma(float* gamma, bool* present) { |
264 | 0 | if (state == Done) { |
265 | 0 | *present = gammaFound; |
266 | 0 | stringstream ss(gammaStr); |
267 | 0 | float val; |
268 | 0 | if (ss >> val) { |
269 | 0 | *gamma = val; |
270 | 0 | return true; |
271 | 0 | } else { |
272 | 0 | return false; |
273 | 0 | } |
274 | 0 | } else { |
275 | 0 | return false; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 0 | bool getOffsetSdr(float* offset_sdr, bool* present) { |
280 | 0 | if (state == Done) { |
281 | 0 | *present = offsetSdrFound; |
282 | 0 | stringstream ss(offsetSdrStr); |
283 | 0 | float val; |
284 | 0 | if (ss >> val) { |
285 | 0 | *offset_sdr = val; |
286 | 0 | return true; |
287 | 0 | } else { |
288 | 0 | return false; |
289 | 0 | } |
290 | 0 | } else { |
291 | 0 | return false; |
292 | 0 | } |
293 | 0 | } |
294 | | |
295 | 0 | bool getOffsetHdr(float* offset_hdr, bool* present) { |
296 | 0 | if (state == Done) { |
297 | 0 | *present = offsetHdrFound; |
298 | 0 | stringstream ss(offsetHdrStr); |
299 | 0 | float val; |
300 | 0 | if (ss >> val) { |
301 | 0 | *offset_hdr = val; |
302 | 0 | return true; |
303 | 0 | } else { |
304 | 0 | return false; |
305 | 0 | } |
306 | 0 | } else { |
307 | 0 | return false; |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | 0 | bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) { |
312 | 0 | if (state == Done) { |
313 | 0 | *present = hdrCapacityMinFound; |
314 | 0 | stringstream ss(hdrCapacityMinStr); |
315 | 0 | float val; |
316 | 0 | if (ss >> val) { |
317 | 0 | *hdr_capacity_min = exp2(val); |
318 | 0 | return true; |
319 | 0 | } else { |
320 | 0 | return false; |
321 | 0 | } |
322 | 0 | } else { |
323 | 0 | return false; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | 4 | bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) { |
328 | 4 | if (state == Done) { |
329 | 4 | *present = hdrCapacityMaxFound; |
330 | 4 | stringstream ss(hdrCapacityMaxStr); |
331 | 4 | float val; |
332 | 4 | if (ss >> val) { |
333 | 0 | *hdr_capacity_max = exp2(val); |
334 | 0 | return true; |
335 | 4 | } else { |
336 | 4 | return false; |
337 | 4 | } |
338 | 4 | } else { |
339 | 0 | return false; |
340 | 0 | } |
341 | 4 | } |
342 | | |
343 | 0 | bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) { |
344 | 0 | if (state == Done) { |
345 | 0 | *present = baseRenditionIsHdrFound; |
346 | 0 | if (!baseRenditionIsHdrStr.compare("False")) { |
347 | 0 | *base_rendition_is_hdr = false; |
348 | 0 | return true; |
349 | 0 | } else if (!baseRenditionIsHdrStr.compare("True")) { |
350 | 0 | *base_rendition_is_hdr = true; |
351 | 0 | return true; |
352 | 0 | } else { |
353 | 0 | return false; |
354 | 0 | } |
355 | 0 | } else { |
356 | 0 | return false; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | private: |
361 | | static const string containerName; |
362 | | |
363 | | static const string versionAttrName; |
364 | | string versionStr; |
365 | | bool versionFound; |
366 | | static const string maxContentBoostAttrName; |
367 | | string maxContentBoostStr; |
368 | | bool maxContentBoostFound; |
369 | | static const string minContentBoostAttrName; |
370 | | string minContentBoostStr; |
371 | | bool minContentBoostFound; |
372 | | static const string gammaAttrName; |
373 | | string gammaStr; |
374 | | bool gammaFound; |
375 | | static const string offsetSdrAttrName; |
376 | | string offsetSdrStr; |
377 | | bool offsetSdrFound; |
378 | | static const string offsetHdrAttrName; |
379 | | string offsetHdrStr; |
380 | | bool offsetHdrFound; |
381 | | static const string hdrCapacityMinAttrName; |
382 | | string hdrCapacityMinStr; |
383 | | bool hdrCapacityMinFound; |
384 | | static const string hdrCapacityMaxAttrName; |
385 | | string hdrCapacityMaxStr; |
386 | | bool hdrCapacityMaxFound; |
387 | | static const string baseRenditionIsHdrAttrName; |
388 | | string baseRenditionIsHdrStr; |
389 | | bool baseRenditionIsHdrFound; |
390 | | |
391 | | string lastAttributeName; |
392 | | ParseState state; |
393 | | }; |
394 | | |
395 | | // GContainer XMP constants - URI and namespace prefix |
396 | | const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; |
397 | | const string kContainerPrefix = "Container"; |
398 | | |
399 | | // GContainer XMP constants - element and attribute names |
400 | | const string kConDirectory = Name(kContainerPrefix, "Directory"); |
401 | | const string kConItem = Name(kContainerPrefix, "Item"); |
402 | | |
403 | | // GContainer XMP constants - names for XMP handlers |
404 | | const string XMPXmlHandler::containerName = "rdf:Description"; |
405 | | // Item XMP constants - URI and namespace prefix |
406 | | const string kItemUri = "http://ns.google.com/photos/1.0/container/item/"; |
407 | | const string kItemPrefix = "Item"; |
408 | | |
409 | | // Item XMP constants - element and attribute names |
410 | | const string kItemLength = Name(kItemPrefix, "Length"); |
411 | | const string kItemMime = Name(kItemPrefix, "Mime"); |
412 | | const string kItemSemantic = Name(kItemPrefix, "Semantic"); |
413 | | |
414 | | // Item XMP constants - element and attribute values |
415 | | const string kSemanticPrimary = "Primary"; |
416 | | const string kSemanticGainMap = "GainMap"; |
417 | | const string kMimeImageJpeg = "image/jpeg"; |
418 | | |
419 | | // GainMap XMP constants - URI and namespace prefix |
420 | | const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/"; |
421 | | const string kGainMapPrefix = "hdrgm"; |
422 | | |
423 | | // GainMap XMP constants - element and attribute names |
424 | | const string kMapVersion = Name(kGainMapPrefix, "Version"); |
425 | | const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin"); |
426 | | const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax"); |
427 | | const string kMapGamma = Name(kGainMapPrefix, "Gamma"); |
428 | | const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR"); |
429 | | const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR"); |
430 | | const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin"); |
431 | | const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax"); |
432 | | const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR"); |
433 | | |
434 | | // GainMap XMP constants - names for XMP handlers |
435 | | const string XMPXmlHandler::versionAttrName = kMapVersion; |
436 | | const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin; |
437 | | const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax; |
438 | | const string XMPXmlHandler::gammaAttrName = kMapGamma; |
439 | | const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr; |
440 | | const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr; |
441 | | const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin; |
442 | | const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax; |
443 | | const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR; |
444 | | |
445 | | uhdr_error_info_t getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, |
446 | 2.77k | uhdr_gainmap_metadata_ext_t* metadata) { |
447 | 2.77k | string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; |
448 | | |
449 | 2.77k | if (xmp_size < nameSpace.size() + 2) { |
450 | 0 | uhdr_error_info_t status; |
451 | 0 | status.error_code = UHDR_CODEC_ERROR; |
452 | 0 | status.has_detail = 1; |
453 | 0 | snprintf(status.detail, sizeof status.detail, |
454 | 0 | "size of xmp block is expected to be atleast %zd bytes, received only %zd bytes", |
455 | 0 | nameSpace.size() + 2, xmp_size); |
456 | 0 | return status; |
457 | 0 | } |
458 | | |
459 | 2.77k | if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) { |
460 | 0 | uhdr_error_info_t status; |
461 | 0 | status.error_code = UHDR_CODEC_ERROR; |
462 | 0 | status.has_detail = 1; |
463 | 0 | snprintf(status.detail, sizeof status.detail, |
464 | 0 | "mismatch in namespace of xmp block. Expected %s, Got %.*s", nameSpace.c_str(), |
465 | 0 | (int)nameSpace.size(), reinterpret_cast<char*>(xmp_data)); |
466 | 0 | return status; |
467 | 0 | } |
468 | | |
469 | | // Position the pointers to the start of XMP XML portion |
470 | 2.77k | xmp_data += nameSpace.size() + 1; |
471 | 2.77k | xmp_size -= nameSpace.size() + 1; |
472 | 2.77k | XMPXmlHandler handler; |
473 | | |
474 | | // xml parser fails to parse packet header, wrapper. remove them before handing the data to |
475 | | // parser. if there is no packet header, do nothing otherwise go to the position of '<' without |
476 | | // '?' after it. |
477 | 2.77k | size_t offset = 0; |
478 | 451k | for (size_t i = 0; i < xmp_size - 1; ++i) { |
479 | 451k | if (xmp_data[i] == '<') { |
480 | 2.76k | if (xmp_data[i + 1] != '?') { |
481 | 2.68k | offset = i; |
482 | 2.68k | break; |
483 | 2.68k | } |
484 | 2.76k | } |
485 | 451k | } |
486 | 2.77k | xmp_data += offset; |
487 | 2.77k | xmp_size -= offset; |
488 | | |
489 | | // If there is no packet wrapper, do nothing other wise go to the position of last '>' without '?' |
490 | | // before it. |
491 | 2.77k | offset = 0; |
492 | 390k | for (size_t i = xmp_size - 1; i >= 1; --i) { |
493 | 390k | if (xmp_data[i] == '>') { |
494 | 2.73k | if (xmp_data[i - 1] != '?') { |
495 | 2.67k | offset = xmp_size - (i + 1); |
496 | 2.67k | break; |
497 | 2.67k | } |
498 | 2.73k | } |
499 | 390k | } |
500 | 2.77k | xmp_size -= offset; |
501 | | |
502 | | // remove padding |
503 | 268k | while (xmp_data[xmp_size - 1] != '>' && xmp_size > 1) { |
504 | 265k | xmp_size--; |
505 | 265k | } |
506 | | |
507 | 2.77k | string str(reinterpret_cast<const char*>(xmp_data), xmp_size); |
508 | 2.77k | MessageHandler msg_handler; |
509 | 2.77k | unique_ptr<XmlRule> rule(new XmlElementRule); |
510 | 2.77k | XmlReader reader(&handler, &msg_handler); |
511 | 2.77k | reader.StartParse(std::move(rule)); |
512 | 2.77k | reader.Parse(str); |
513 | 2.77k | reader.FinishParse(); |
514 | 2.77k | if (reader.HasErrors()) { |
515 | 2.73k | uhdr_error_info_t status; |
516 | 2.73k | status.error_code = UHDR_CODEC_UNKNOWN_ERROR; |
517 | 2.73k | status.has_detail = 1; |
518 | 2.73k | snprintf(status.detail, sizeof status.detail, "xml parser returned with error"); |
519 | 2.73k | return status; |
520 | 2.73k | } |
521 | | |
522 | | // Apply default values to any not-present fields, except for Version, |
523 | | // maxContentBoost, and hdrCapacityMax, which are required. Return false if |
524 | | // we encounter a present field that couldn't be parsed, since this |
525 | | // indicates it is invalid (eg. string where there should be a float). |
526 | 36 | bool present = false; |
527 | 36 | if (!handler.getVersion(&metadata->version, &present) || !present) { |
528 | 30 | uhdr_error_info_t status; |
529 | 30 | status.error_code = UHDR_CODEC_ERROR; |
530 | 30 | status.has_detail = 1; |
531 | 30 | snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s", |
532 | 30 | kMapVersion.c_str()); |
533 | 30 | return status; |
534 | 30 | } |
535 | 6 | if (!handler.getMaxContentBoost(&metadata->max_content_boost[0], &present) || !present) { |
536 | 2 | uhdr_error_info_t status; |
537 | 2 | status.error_code = UHDR_CODEC_ERROR; |
538 | 2 | status.has_detail = 1; |
539 | 2 | snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s", |
540 | 2 | kMapGainMapMax.c_str()); |
541 | 2 | return status; |
542 | 2 | } |
543 | 4 | if (!handler.getHdrCapacityMax(&metadata->hdr_capacity_max, &present) || !present) { |
544 | 4 | uhdr_error_info_t status; |
545 | 4 | status.error_code = UHDR_CODEC_ERROR; |
546 | 4 | status.has_detail = 1; |
547 | 4 | snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s", |
548 | 4 | kMapHDRCapacityMax.c_str()); |
549 | 4 | return status; |
550 | 4 | } |
551 | 0 | if (!handler.getMinContentBoost(&metadata->min_content_boost[0], &present)) { |
552 | 0 | if (present) { |
553 | 0 | uhdr_error_info_t status; |
554 | 0 | status.error_code = UHDR_CODEC_ERROR; |
555 | 0 | status.has_detail = 1; |
556 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
557 | 0 | kMapGainMapMin.c_str()); |
558 | 0 | return status; |
559 | 0 | } |
560 | 0 | metadata->min_content_boost[0] = 1.0f; |
561 | 0 | } |
562 | 0 | if (!handler.getGamma(&metadata->gamma[0], &present)) { |
563 | 0 | if (present) { |
564 | 0 | uhdr_error_info_t status; |
565 | 0 | status.error_code = UHDR_CODEC_ERROR; |
566 | 0 | status.has_detail = 1; |
567 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
568 | 0 | kMapGamma.c_str()); |
569 | 0 | return status; |
570 | 0 | } |
571 | 0 | metadata->gamma[0] = 1.0f; |
572 | 0 | } |
573 | 0 | if (!handler.getOffsetSdr(&metadata->offset_sdr[0], &present)) { |
574 | 0 | if (present) { |
575 | 0 | uhdr_error_info_t status; |
576 | 0 | status.error_code = UHDR_CODEC_ERROR; |
577 | 0 | status.has_detail = 1; |
578 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
579 | 0 | kMapOffsetSdr.c_str()); |
580 | 0 | return status; |
581 | 0 | } |
582 | 0 | metadata->offset_sdr[0] = 1.0f / 64.0f; |
583 | 0 | } |
584 | 0 | if (!handler.getOffsetHdr(&metadata->offset_hdr[0], &present)) { |
585 | 0 | if (present) { |
586 | 0 | uhdr_error_info_t status; |
587 | 0 | status.error_code = UHDR_CODEC_ERROR; |
588 | 0 | status.has_detail = 1; |
589 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
590 | 0 | kMapOffsetHdr.c_str()); |
591 | 0 | return status; |
592 | 0 | } |
593 | 0 | metadata->offset_hdr[0] = 1.0f / 64.0f; |
594 | 0 | } |
595 | 0 | if (!handler.getHdrCapacityMin(&metadata->hdr_capacity_min, &present)) { |
596 | 0 | if (present) { |
597 | 0 | uhdr_error_info_t status; |
598 | 0 | status.error_code = UHDR_CODEC_ERROR; |
599 | 0 | status.has_detail = 1; |
600 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
601 | 0 | kMapHDRCapacityMin.c_str()); |
602 | 0 | return status; |
603 | 0 | } |
604 | 0 | metadata->hdr_capacity_min = 1.0f; |
605 | 0 | } |
606 | | |
607 | 0 | bool base_rendition_is_hdr; |
608 | 0 | if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) { |
609 | 0 | if (present) { |
610 | 0 | uhdr_error_info_t status; |
611 | 0 | status.error_code = UHDR_CODEC_ERROR; |
612 | 0 | status.has_detail = 1; |
613 | 0 | snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s", |
614 | 0 | kMapBaseRenditionIsHDR.c_str()); |
615 | 0 | return status; |
616 | 0 | } |
617 | 0 | base_rendition_is_hdr = false; |
618 | 0 | } |
619 | 0 | if (base_rendition_is_hdr) { |
620 | 0 | uhdr_error_info_t status; |
621 | 0 | status.error_code = UHDR_CODEC_ERROR; |
622 | 0 | status.has_detail = 1; |
623 | 0 | snprintf(status.detail, sizeof status.detail, "hdr intent as base rendition is not supported"); |
624 | 0 | return status; |
625 | 0 | } |
626 | 0 | metadata->use_base_cg = true; |
627 | 0 | std::fill_n(metadata->min_content_boost + 1, 2, metadata->min_content_boost[0]); |
628 | 0 | std::fill_n(metadata->max_content_boost + 1, 2, metadata->max_content_boost[0]); |
629 | 0 | std::fill_n(metadata->gamma + 1, 2, metadata->gamma[0]); |
630 | 0 | std::fill_n(metadata->offset_hdr + 1, 2, metadata->offset_hdr[0]); |
631 | 0 | std::fill_n(metadata->offset_sdr + 1, 2, metadata->offset_sdr[0]); |
632 | |
|
633 | 0 | return g_no_error; |
634 | 0 | } |
635 | | |
636 | | string generateXmpForPrimaryImage(size_t secondary_image_length, |
637 | 0 | uhdr_gainmap_metadata_ext_t& metadata) { |
638 | 0 | const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")}); |
639 | 0 | const vector<string> kLiItem({string("rdf:li"), kConItem}); |
640 | |
|
641 | 0 | std::stringstream ss; |
642 | 0 | photos_editing_formats::image_io::XmlWriter writer(ss); |
643 | 0 | writer.StartWritingElement("x:xmpmeta"); |
644 | 0 | writer.WriteXmlns("x", "adobe:ns:meta/"); |
645 | 0 | writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2"); |
646 | 0 | writer.StartWritingElement("rdf:RDF"); |
647 | 0 | writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); |
648 | 0 | writer.StartWritingElement("rdf:Description"); |
649 | 0 | writer.WriteXmlns(kContainerPrefix, kContainerUri); |
650 | 0 | writer.WriteXmlns(kItemPrefix, kItemUri); |
651 | 0 | writer.WriteXmlns(kGainMapPrefix, kGainMapUri); |
652 | 0 | writer.WriteAttributeNameAndValue(kMapVersion, metadata.version); |
653 | |
|
654 | 0 | writer.StartWritingElements(kConDirSeq); |
655 | |
|
656 | 0 | size_t item_depth = writer.StartWritingElement("rdf:li"); |
657 | 0 | writer.WriteAttributeNameAndValue("rdf:parseType", "Resource"); |
658 | 0 | writer.StartWritingElement(kConItem); |
659 | 0 | writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary); |
660 | 0 | writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg); |
661 | 0 | writer.FinishWritingElementsToDepth(item_depth); |
662 | |
|
663 | 0 | writer.StartWritingElement("rdf:li"); |
664 | 0 | writer.WriteAttributeNameAndValue("rdf:parseType", "Resource"); |
665 | 0 | writer.StartWritingElement(kConItem); |
666 | 0 | writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap); |
667 | 0 | writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg); |
668 | 0 | writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); |
669 | |
|
670 | 0 | writer.FinishWriting(); |
671 | |
|
672 | 0 | return ss.str(); |
673 | 0 | } |
674 | | |
675 | 0 | string generateXmpForSecondaryImage(uhdr_gainmap_metadata_ext_t& metadata) { |
676 | 0 | const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")}); |
677 | |
|
678 | 0 | std::stringstream ss; |
679 | 0 | photos_editing_formats::image_io::XmlWriter writer(ss); |
680 | 0 | writer.StartWritingElement("x:xmpmeta"); |
681 | 0 | writer.WriteXmlns("x", "adobe:ns:meta/"); |
682 | 0 | writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2"); |
683 | 0 | writer.StartWritingElement("rdf:RDF"); |
684 | 0 | writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); |
685 | 0 | writer.StartWritingElement("rdf:Description"); |
686 | 0 | writer.WriteXmlns(kGainMapPrefix, kGainMapUri); |
687 | 0 | writer.WriteAttributeNameAndValue(kMapVersion, metadata.version); |
688 | 0 | writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.min_content_boost[0])); |
689 | 0 | writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.max_content_boost[0])); |
690 | 0 | writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma[0]); |
691 | 0 | writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offset_sdr[0]); |
692 | 0 | writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offset_hdr[0]); |
693 | 0 | writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdr_capacity_min)); |
694 | 0 | writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdr_capacity_max)); |
695 | 0 | writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False"); |
696 | 0 | writer.FinishWriting(); |
697 | |
|
698 | 0 | return ss.str(); |
699 | 0 | } |
700 | | |
701 | | } // namespace ultrahdr |