1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "hdr_helper.h"
17
18 #include <dlfcn.h>
19 #include <securec.h>
20
21 #include "hilog/log.h"
22 #include "log_tags.h"
23 #include "image_log.h"
24 #include "jpeg_mpf_parser.h"
25 #include "image_utils.h"
26 #include "media_errors.h"
27 #include "src/codec/SkJpegCodec.h"
28 #include "src/codec/SkJpegDecoderMgr.h"
29 #ifdef HEIF_HW_DECODE_ENABLE
30 #include "heif_impl/HeifDecoder.h"
31 #endif
32 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
33 #include "v1_0/hdr_static_metadata.h"
34 #endif
35
36 #undef LOG_DOMAIN
37 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
38
39 #undef LOG_TAG
40 #define LOG_TAG "HdrHelper"
41
42 namespace OHOS {
43 namespace ImagePlugin {
44 using namespace std;
45 using namespace Media;
46 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
47 using namespace HDI::Display::Graphic::Common::V1_0;
48 #endif
49 constexpr uint8_t JPEG_MARKER_PREFIX = 0xFF;
50 constexpr uint8_t JPEG_MARKER_APP0 = 0xE0;
51
52 constexpr uint8_t JPEG_MARKER_APP2 = 0xE2;
53 constexpr uint8_t JPEG_MARKER_APP5 = 0xE5;
54 constexpr uint8_t JPEG_MARKER_APP8 = 0xE8;
55 constexpr uint8_t JPEG_MARKER_APP11 = 0xEB;
56 constexpr uint8_t JPEG_SOI = 0xD8;
57 constexpr uint32_t MOVE_ONE_BYTE = 8;
58 constexpr uint32_t VIVID_BASE_IMAGE_MARKER_SIZE = 22;
59 constexpr int JPEG_MARKER_LENGTH_SIZE = 2;
60 constexpr int JPEG_MARKER_TAG_SIZE = 2;
61 constexpr int MPF_TAG_SIZE = 4;
62 constexpr uint32_t UINT8_BYTE_COUNT = 1;
63 constexpr uint32_t UINT16_BYTE_COUNT = 2;
64 constexpr uint32_t UINT32_BYTE_COUNT = 4;
65 constexpr uint8_t JPEG_IMAGE_NUM = 2;
66 constexpr uint8_t VIVID_FILE_TYPE_BASE = 1;
67 constexpr uint8_t VIVID_FILE_TYPE_GAINMAP = 2;
68 constexpr uint8_t EMPTY_SIZE = 0;
69 constexpr uint8_t VIVID_STATIC_METADATA_SIZE_IN_IMAGE = 28;
70 constexpr uint8_t ITUT35_TAG_SIZE = 6;
71 constexpr uint8_t INDEX_ZERO = 0;
72 constexpr uint8_t INDEX_ONE = 1;
73 constexpr uint8_t INDEX_TWO = 2;
74 constexpr uint8_t INDEX_THREE = 3;
75 constexpr uint8_t COLOR_INFO_BYTES = 3;
76 constexpr uint8_t ONE_COMPONENT = 1;
77 constexpr uint8_t THREE_COMPONENTS = 3;
78 constexpr uint8_t THREE_GAINMAP_CHANNEL = 3;
79 constexpr uint8_t VIVID_PRE_INFO_SIZE = 10;
80 constexpr uint8_t VIVID_METADATA_PRE_INFO_SIZE = 10;
81 constexpr uint32_t HDR_MULTI_PICTURE_APP_LENGTH = 90;
82 constexpr uint32_t EXTEND_INFO_MAIN_SIZE = 60;
83 constexpr uint32_t ISO_GAINMAP_METADATA_PAYLOAD_MIN_SIZE = 38;
84 constexpr uint32_t DENOMINATOR = 1000000;
85 constexpr uint16_t EMPTY_META_SIZE = 0;
86 constexpr uint8_t FIRST_CHANNEL_INDEX = 0;
87
88 const float SM_COLOR_SCALE = 0.00002f;
89 const float SM_LUM_SCALE = 0.0001f;
90
91 constexpr static int JPEG_MARKER_LENGTH = 2;
92 static constexpr uint8_t JPEG_SOI_HEADER[] = { 0xFF, 0xD8 };
93
94 static constexpr uint8_t ITUT35_TAG[ITUT35_TAG_SIZE] = {
95 'I', 'T', 'U', 'T', '3', '5',
96 };
97
98 constexpr uint8_t ISO_GAINMAP_TAG_SIZE = 28;
99 constexpr uint8_t ISO_GAINMAP_TAG[ISO_GAINMAP_TAG_SIZE] = {
100 'u', 'r', 'n', ':', 'i', 's', 'o', ':', 's', 't', 'd', ':', 'i', 's', 'o', ':', 't', 's', ':', '2', '1', '4', '9',
101 '6', ':', '-', '1', '\0'
102 };
103 constexpr uint8_t HDR_MEDIA_TYPE_TAG_SIZE = 45;
104 constexpr uint8_t HDR_MEDIA_TYPE_TAG[HDR_MEDIA_TYPE_TAG_SIZE] = {
105 'u', 'r', 'n', ':', 'h', 'a', 'r', 'm', 'o', 'n', 'y', 'o', 's', ':', 'm', 'u', 'l', 't', 'i', 'm', 'e',
106 'd', 'i', 'a', ':', 'i', 'm', 'a', 'g', 'e', ':', 'v', 'i', 'd', 'e', 'o', 'c', 'o', 'v', 'e', 'r', ':',
107 'v', '1', '\0'
108 };
109
110 #ifdef HEIF_HW_DECODE_ENABLE
111 static const map<HeifImageHdrType, ImageHdrType> HEIF_HDR_TYPE_MAP = {
112 { HeifImageHdrType::UNKNOWN, ImageHdrType::UNKNOWN},
113 { HeifImageHdrType::VIVID_DUAL, ImageHdrType::HDR_VIVID_DUAL},
114 { HeifImageHdrType::ISO_DUAL, ImageHdrType::HDR_ISO_DUAL},
115 { HeifImageHdrType::VIVID_SINGLE, ImageHdrType::HDR_VIVID_SINGLE},
116 { HeifImageHdrType::ISO_SINGLE, ImageHdrType::HDR_ISO_SINGLE},
117 };
118 #endif
119
120 typedef bool (*GetCuvaGainMapOffsetT)(jpeg_marker_struct* marker, uint32_t appSize, uint32_t& offset);
121 typedef bool (*GetCuvaGainMapMetadataT)(jpeg_marker_struct* marker, std::vector<uint8_t>& metadata);
122
123 struct ColorInfo {
124 uint8_t primary;
125 uint8_t transFunc;
126 uint8_t model;
127 };
128
129 struct ExtendInfoMain {
130 float gainMapMax[3];
131 float gainMapMin[3];
132 float gamma[3];
133 float baseSdrImageOffset[3];
134 float altHdrImageOffset[3];
135 };
136
137 struct TransformInfo {
138 uint8_t mappingFlag;
139 std::vector<uint8_t> mapping;
140 };
141
142 struct ExtendInfoExtention {
143 ColorInfo baseColor;
144 ColorInfo enhanceDataColor;
145 ColorInfo combineColor;
146 ColorInfo enhanceColor;
147 TransformInfo baseMapping;
148 TransformInfo combineMapping;
149 };
150
GetVividJpegGainMapOffset(const vector<jpeg_marker_struct * > & markerList,vector<uint32_t> preOffsets,uint32_t & offset)151 static bool GetVividJpegGainMapOffset(const vector<jpeg_marker_struct*>& markerList, vector<uint32_t> preOffsets,
152 uint32_t& offset)
153 {
154 if (markerList.size() == EMPTY_SIZE) {
155 return false;
156 }
157 for (uint32_t i = 0; i < markerList.size(); i++) {
158 jpeg_marker_struct* marker = markerList[i];
159 uint32_t size = marker->data_length;
160 if (JPEG_MARKER_APP8 != marker->marker || marker->data_length < VIVID_BASE_IMAGE_MARKER_SIZE) {
161 continue;
162 }
163 uint8_t* data = marker->data;
164 uint32_t dataOffset = 0;
165 if (memcmp(marker->data, ITUT35_TAG, sizeof(ITUT35_TAG)) != EOK) {
166 continue;
167 }
168 dataOffset += ITUT35_TAG_SIZE;
169 uint8_t itut35CountryCode = data[dataOffset++];
170 uint16_t terminalProvideCode = ImageUtils::BytesToUint16(data, dataOffset, size);
171 uint16_t terminalProvideOrientedCode = ImageUtils::BytesToUint16(data, dataOffset, size);
172 IMAGE_LOGD("vivid base info countryCode=%{public}d,terminalCode=%{public}d,orientedcode=%{public}d",
173 itut35CountryCode, terminalProvideCode, terminalProvideOrientedCode);
174 uint8_t extendedFrameNumber = data[dataOffset++];
175 if (extendedFrameNumber < JPEG_IMAGE_NUM) {
176 continue;
177 }
178 uint8_t fileType = data[dataOffset++];
179 uint8_t metaType = data[dataOffset++];
180 uint8_t enhanceType = data[dataOffset++];
181 uint8_t hdrType = data[dataOffset++];
182 IMAGE_LOGD("vivid base info metaType=%{public}d, enhanceType=%{public}d, hdrType=%{public}d",
183 metaType, enhanceType, hdrType);
184 if (fileType != VIVID_FILE_TYPE_BASE) {
185 continue;
186 }
187 uint32_t originOffset = ImageUtils::BytesToUint32(data, dataOffset, size);
188 offset = originOffset + preOffsets[i];
189 uint32_t relativeOffset = ImageUtils::BytesToUint32(data, dataOffset, size); // offset minus all app size
190 IMAGE_LOGD("vivid base info originOffset=%{public}d, relativeOffset=%{public}d, offset=%{public}d",
191 originOffset, relativeOffset, offset);
192 return true;
193 }
194 return false;
195 }
196
GetCuvaJpegGainMapOffset(vector<jpeg_marker_struct * > & markerList,uint32_t appSize,uint32_t & offset)197 static bool GetCuvaJpegGainMapOffset(vector<jpeg_marker_struct*>& markerList,
198 uint32_t appSize, uint32_t& offset) __attribute__((no_sanitize("cfi")))
199 {
200 if (markerList.size() == EMPTY_SIZE) {
201 return false;
202 }
203 auto handle = dlopen("libimage_cuva_parser.z.so", RTLD_LAZY);
204 if (!handle) {
205 dlclose(handle);
206 return false;
207 }
208 GetCuvaGainMapOffsetT check = reinterpret_cast<GetCuvaGainMapOffsetT>(dlsym(handle, "GetCuvaGainMapOffset"));
209 if (!check) {
210 dlclose(handle);
211 return false;
212 }
213 bool result = false;
214 for (auto marker : markerList) {
215 if (marker == nullptr || marker->data == nullptr || JPEG_MARKER_APP5 != marker->marker) {
216 continue;
217 }
218 result = check(marker, appSize, offset);
219 if (result) {
220 break;
221 }
222 }
223 dlclose(handle);
224 return result;
225 }
226
ParseMpfOffset(jpeg_marker_struct * marker,uint32_t preOffset)227 static vector<uint32_t> ParseMpfOffset(jpeg_marker_struct* marker, uint32_t preOffset)
228 {
229 if (JPEG_MARKER_APP2 != marker->marker) {
230 return {};
231 }
232 auto jpegMpf = std::make_unique<JpegMpfParser>();
233 if (!jpegMpf->Parsing(marker->data, marker->data_length)) {
234 return {};
235 }
236 uint32_t imageNum = jpegMpf->images_.size();
237 if (imageNum < JPEG_IMAGE_NUM) {
238 return {};
239 }
240 vector<uint32_t> offsetArray(imageNum);
241 // gain map offset need add Mpf marker offset;
242 uint32_t markerHeaderOffset = preOffset + JPEG_MARKER_TAG_SIZE + JPEG_MARKER_LENGTH_SIZE + MPF_TAG_SIZE;
243 for (uint32_t i = INDEX_ONE; i < imageNum; i++) {
244 offsetArray[i] = jpegMpf->images_[i].offset + markerHeaderOffset;
245 }
246 return offsetArray;
247 }
248
ParseBaseISOTag(jpeg_marker_struct * marker)249 static bool ParseBaseISOTag(jpeg_marker_struct* marker)
250 {
251 bool cond = (JPEG_MARKER_APP2 != marker->marker);
252 CHECK_ERROR_RETURN_RET(cond, false);
253 cond = (marker->data_length <= ISO_GAINMAP_TAG_SIZE);
254 CHECK_ERROR_RETURN_RET(cond, false);
255 cond = (memcmp(marker->data, ISO_GAINMAP_TAG, ISO_GAINMAP_TAG_SIZE) == EOK);
256 CHECK_ERROR_RETURN_RET(cond, true);
257 return false;
258 }
259
GetISOJpegGainMapOffset(vector<jpeg_marker_struct * > & markerList,vector<uint32_t> preOffsets,uint32_t & offset)260 static bool GetISOJpegGainMapOffset(vector<jpeg_marker_struct*>& markerList,
261 vector<uint32_t> preOffsets, uint32_t& offset)
262 {
263 if (markerList.size() == EMPTY_SIZE) {
264 return false;
265 }
266 vector<uint32_t> offsetArray;
267 bool isoTag = false;
268 for (uint32_t i = 0; i < markerList.size(); i++) {
269 jpeg_marker_struct* marker = markerList[i];
270 if (JPEG_MARKER_APP2 != marker->marker) {
271 continue;
272 }
273 uint32_t markerOffset = preOffsets[i];
274 if (offsetArray.size() == EMPTY_SIZE) {
275 offsetArray = ParseMpfOffset(marker, markerOffset);
276 }
277 if (!isoTag) {
278 isoTag = ParseBaseISOTag(marker);
279 }
280 }
281 if (isoTag && offsetArray.size() == INDEX_TWO) {
282 offset = offsetArray[INDEX_ONE];
283 return true;
284 }
285 return false;
286 }
287
IsVaildHdrMediaType(jpeg_marker_struct * marker)288 static bool IsVaildHdrMediaType(jpeg_marker_struct* marker)
289 {
290 IMAGE_LOGI("HDR-IMAGE seek HdrMediaType");
291 if (marker == nullptr || JPEG_MARKER_APP11 != marker->marker) {
292 IMAGE_LOGI("HDR-IMAGE marker == nullptr");
293 return false;
294 }
295 if (marker->data_length <= HDR_MEDIA_TYPE_TAG_SIZE ||
296 memcmp(marker->data, HDR_MEDIA_TYPE_TAG, HDR_MEDIA_TYPE_TAG_SIZE) != 0) {
297 IMAGE_LOGE("HDR-IMAGE cmp media marker failed");
298 return false;
299 }
300 return true;
301 }
302
CheckJpegGainMapHdrType(SkJpegCodec * jpegCodec,uint32_t & offset)303 static ImageHdrType CheckJpegGainMapHdrType(SkJpegCodec* jpegCodec,
304 uint32_t& offset) __attribute__((no_sanitize("cfi")))
305 {
306 uint32_t allAppSize = JPEG_MARKER_TAG_SIZE;
307 vector<jpeg_marker_struct*> vividMarkerList;
308 vector<jpeg_marker_struct*> cuvaMarkerList;
309 vector<jpeg_marker_struct*> isoMarkerList;
310 vector<uint32_t> isoPreMarkerOffset;
311 vector<uint32_t> vividPreMarkerOffset;
312 jpeg_marker_struct* mediaMarker;
313 for (jpeg_marker_struct* marker = jpegCodec->decoderMgr()->dinfo()->marker_list; marker; marker = marker->next) {
314 if (JPEG_MARKER_APP8 == marker->marker) {
315 vividMarkerList.push_back(marker);
316 vividPreMarkerOffset.push_back(allAppSize);
317 }
318 if (JPEG_MARKER_APP5 == marker->marker) {
319 cuvaMarkerList.push_back(marker);
320 }
321 if (JPEG_MARKER_APP2 == marker->marker) {
322 isoMarkerList.push_back(marker);
323 isoPreMarkerOffset.push_back(allAppSize);
324 }
325 if (JPEG_MARKER_APP0 == (marker->marker & 0xF0)) {
326 allAppSize += marker->data_length + JPEG_MARKER_TAG_SIZE + JPEG_MARKER_LENGTH_SIZE;
327 }
328 if (JPEG_MARKER_APP11 == marker->marker) {
329 IMAGE_LOGI("HDR-IMAGE find app11");
330 mediaMarker = marker;
331 }
332 }
333 if (GetVividJpegGainMapOffset(vividMarkerList, vividPreMarkerOffset, offset)) {
334 uint32_t tmpOffset = 0;
335 GetISOJpegGainMapOffset(isoMarkerList, isoPreMarkerOffset, offset);
336 if (tmpOffset > 0 && tmpOffset != offset) {
337 IMAGE_LOGD("vivid gainmap offset from mpf, offset:%{public}d-%{public}d", tmpOffset, offset);
338 offset = tmpOffset;
339 }
340 return ImageHdrType::HDR_VIVID_DUAL;
341 }
342 if (GetCuvaJpegGainMapOffset(cuvaMarkerList, allAppSize, offset)) {
343 return ImageHdrType::HDR_CUVA;
344 }
345 bool cond = GetISOJpegGainMapOffset(isoMarkerList, isoPreMarkerOffset, offset);
346 auto ret = ImageHdrType::HDR_ISO_DUAL;
347 CHECK_ERROR_RETURN_RET(cond, ret);
348 if (IsVaildHdrMediaType(mediaMarker)) {
349 IMAGE_LOGI("HDR-IMAGE SUCCESS");
350 return ImageHdrType::HDR_LOG_DUAL;
351 }
352 return ImageHdrType::SDR;
353 }
354
355 #ifdef HEIF_HW_DECODE_ENABLE
CheckHeifHdrType(HeifDecoder * decoder)356 static ImageHdrType CheckHeifHdrType(HeifDecoder* decoder)
357 {
358 auto ret = ImageHdrType::UNKNOWN;
359 bool cond = decoder == nullptr;
360 CHECK_ERROR_RETURN_RET(cond, ret);
361 HeifImageHdrType heifHdrType = decoder->getHdrType();
362 auto findItem = std::find_if(HEIF_HDR_TYPE_MAP.begin(), HEIF_HDR_TYPE_MAP.end(),
363 [heifHdrType](const map<HeifImageHdrType, ImageHdrType>::value_type item) {
364 return item.first == heifHdrType;
365 });
366 cond = (findItem == HEIF_HDR_TYPE_MAP.end());
367 CHECK_ERROR_RETURN_RET(cond, ImageHdrType::UNKNOWN);
368 return findItem->second;
369 }
370 #endif
371
CheckHdrType(SkCodec * codec,uint32_t & offset)372 ImageHdrType HdrHelper::CheckHdrType(SkCodec* codec, uint32_t& offset) __attribute__((no_sanitize("cfi")))
373 {
374 offset = 0;
375 ImageHdrType type = ImageHdrType::SDR;
376 bool cond = codec == nullptr;
377 CHECK_ERROR_RETURN_RET(cond, type);
378 switch (codec->getEncodedFormat()) {
379 case SkEncodedImageFormat::kJPEG: {
380 SkJpegCodec* jpegCodec = static_cast<SkJpegCodec*>(codec);
381 if (jpegCodec == nullptr || jpegCodec->decoderMgr() == nullptr) {
382 break;
383 }
384 type = CheckJpegGainMapHdrType(jpegCodec, offset);
385 break;
386 }
387 case SkEncodedImageFormat::kHEIF: {
388 #ifdef HEIF_HW_DECODE_ENABLE
389 auto decoder = reinterpret_cast<HeifDecoder*>(codec->getHeifContext());
390 if (decoder == nullptr) {
391 break;
392 }
393 type = CheckHeifHdrType(decoder);
394 #endif
395 break;
396 }
397 default:
398 type = ImageHdrType::SDR;
399 break;
400 }
401 return type;
402 }
403
CheckGainmapOffset(ImageHdrType type,InputDataStream * stream,uint32_t & offset)404 bool HdrHelper::CheckGainmapOffset(ImageHdrType type, InputDataStream* stream, uint32_t& offset)
405 {
406 if (type == Media::ImageHdrType::HDR_LOG_DUAL) {
407 return true;
408 }
409 if (stream == nullptr) {
410 return false;
411 }
412
413 uint32_t streamSize = stream->GetStreamSize();
414 if (offset >= streamSize || JPEG_MARKER_LENGTH > (streamSize - offset)) {
415 IMAGE_LOGE("HDR-IMAGE CheckHdrType invalid offset %{public}d for stream size %{public}d", offset, streamSize);
416 return false;
417 }
418
419 uint8_t *outBuffer = stream->GetDataPtr();
420 if (outBuffer == nullptr) {
421 IMAGE_LOGE("HDR-IMAGE CheckHdrTYpe null data pointer");
422 return false;
423 }
424
425 if (std::memcmp(JPEG_SOI_HEADER, outBuffer + offset, JPEG_MARKER_LENGTH) != 0) {
426 IMAGE_LOGE("HDR-IMAGE CheckHdrType gainmap memcpy SOI error");
427 return false;
428 }
429 return true;
430 }
431
ParseVividJpegStaticMetadata(uint8_t * data,uint32_t & offset,uint32_t size,vector<uint8_t> & staticMetaVec)432 static bool ParseVividJpegStaticMetadata(uint8_t* data, uint32_t& offset, uint32_t size, vector<uint8_t>& staticMetaVec)
433 {
434 #if defined(_WIN32) || defined(_APPLE) || defined(IOS_PLATFORM) || defined(ANDROID_PLATFORM)
435 return false;
436 #else
437 uint16_t staticMetadataSize = ImageUtils::BytesToUint16(data, offset, size);
438 if (staticMetadataSize == EMPTY_SIZE) {
439 staticMetaVec.resize(EMPTY_SIZE);
440 return true;
441 }
442 bool cond = staticMetadataSize > size || staticMetadataSize < VIVID_STATIC_METADATA_SIZE_IN_IMAGE;
443 CHECK_ERROR_RETURN_RET(cond, false);
444
445 HdrStaticMetadata staticMeta{};
446 staticMeta.smpte2086.displayPrimaryRed.x = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
447 staticMeta.smpte2086.displayPrimaryRed.y = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
448 staticMeta.smpte2086.displayPrimaryGreen.x = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
449 staticMeta.smpte2086.displayPrimaryGreen.y = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
450 staticMeta.smpte2086.displayPrimaryBlue.x = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
451 staticMeta.smpte2086.displayPrimaryBlue.y = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
452 staticMeta.smpte2086.whitePoint.x = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
453 staticMeta.smpte2086.whitePoint.y = (float)ImageUtils::BytesToUint16(data, offset, size) * SM_COLOR_SCALE;
454 staticMeta.smpte2086.maxLuminance = (float)ImageUtils::BytesToUint32(data, offset, size);
455 staticMeta.smpte2086.minLuminance = (float)ImageUtils::BytesToUint32(data, offset, size) * SM_LUM_SCALE;
456 staticMeta.cta861.maxContentLightLevel = (float)ImageUtils::BytesToUint16(data, offset, size);
457 staticMeta.cta861.maxFrameAverageLightLevel = (float)ImageUtils::BytesToUint16(data, offset, size);
458 uint32_t vecSize = sizeof(HdrStaticMetadata);
459 staticMetaVec.resize(vecSize);
460 if (memcpy_s(staticMetaVec.data(), vecSize, &staticMeta, vecSize) != EOK) {
461 return false;
462 }
463 if (staticMetadataSize > VIVID_STATIC_METADATA_SIZE_IN_IMAGE) {
464 offset += (staticMetadataSize - VIVID_STATIC_METADATA_SIZE_IN_IMAGE);
465 }
466 return true;
467 #endif
468 }
469
ReplaceZeroData(float num,float replacement)470 static float ReplaceZeroData(float num, float replacement)
471 {
472 return (num == 0.0f) ? replacement : num;
473 }
474
DuplicateSingleChannelMetadataToTriple(ISOMetadata & metaISO)475 static void DuplicateSingleChannelMetadataToTriple(ISOMetadata& metaISO)
476 {
477 for (size_t i = GAINMAP_CHANNEL_NUM_ONE; i < GAINMAP_CHANNEL_NUM_THREE; i++) {
478 metaISO.enhanceClippedThreholdMaxGainmap[i] = ReplaceZeroData(metaISO.enhanceClippedThreholdMaxGainmap[i],
479 metaISO.enhanceClippedThreholdMaxGainmap[FIRST_CHANNEL_INDEX]);
480 metaISO.enhanceMappingGamma[i] = ReplaceZeroData(metaISO.enhanceMappingGamma[i],
481 metaISO.enhanceMappingGamma[FIRST_CHANNEL_INDEX]);
482 metaISO.enhanceMappingBaselineOffset[i] = ReplaceZeroData(metaISO.enhanceMappingBaselineOffset[i],
483 metaISO.enhanceMappingBaselineOffset[FIRST_CHANNEL_INDEX]);
484 metaISO.enhanceMappingAlternateOffset[i] = ReplaceZeroData(metaISO.enhanceMappingAlternateOffset[i],
485 metaISO.enhanceMappingAlternateOffset[FIRST_CHANNEL_INDEX]);
486 IMAGE_LOGD("HDR-IMAGE fix MaxGainmap %{public}f", metaISO.enhanceClippedThreholdMaxGainmap[i]);
487 IMAGE_LOGD("HDR-IMAGE fix Gamma %{public}f", metaISO.enhanceMappingGamma[i]);
488 IMAGE_LOGD("HDR-IMAGE fix baseOffset %{public}f", metaISO.enhanceMappingBaselineOffset[i]);
489 IMAGE_LOGD("HDR-IMAGE fix alternateOffset %{public}f", metaISO.enhanceMappingAlternateOffset[i]);
490 }
491 }
492
ValidateAndCorrectISOMetadata(ISOMetadata & metaISO,Media::ImageHdrType type)493 static void ValidateAndCorrectISOMetadata(ISOMetadata& metaISO, Media::ImageHdrType type)
494 {
495 if (type != ImageHdrType::HDR_VIVID_DUAL && type != ImageHdrType::HDR_VIVID_SINGLE) {
496 return;
497 }
498 DuplicateSingleChannelMetadataToTriple(metaISO);
499 }
500
ValidateAndCorrectMetaData(Media::HdrMetadata & metadata,Media::ImageHdrType type)501 void HdrHelper::ValidateAndCorrectMetaData(Media::HdrMetadata& metadata, Media::ImageHdrType type)
502 {
503 ValidateAndCorrectISOMetadata(metadata.extendMeta.metaISO, type);
504 }
505
ParseExtendInfoMain(uint8_t * data,uint32_t & offset,uint32_t size,bool isThreeCom)506 static ExtendInfoMain ParseExtendInfoMain(uint8_t* data, uint32_t& offset, uint32_t size, bool isThreeCom)
507 {
508 ExtendInfoMain infoMain{};
509 infoMain.gainMapMin[INDEX_ZERO] = ImageUtils::BytesToFloat(data, offset, size);
510 infoMain.gainMapMax[INDEX_ZERO] = ImageUtils::BytesToFloat(data, offset, size);
511 infoMain.gamma[INDEX_ZERO] = ImageUtils::BytesToFloat(data, offset, size);
512 infoMain.baseSdrImageOffset[INDEX_ZERO] = ImageUtils::BytesToFloat(data, offset, size);
513 infoMain.altHdrImageOffset[INDEX_ZERO] = ImageUtils::BytesToFloat(data, offset, size);
514 if (isThreeCom) {
515 infoMain.gainMapMin[INDEX_ONE] = ImageUtils::BytesToFloat(data, offset, size);
516 infoMain.gainMapMax[INDEX_ONE] = ImageUtils::BytesToFloat(data, offset, size);
517 infoMain.gamma[INDEX_ONE] = ImageUtils::BytesToFloat(data, offset, size);
518 infoMain.baseSdrImageOffset[INDEX_ONE] = ImageUtils::BytesToFloat(data, offset, size);
519 infoMain.altHdrImageOffset[INDEX_ONE] = ImageUtils::BytesToFloat(data, offset, size);
520 infoMain.gainMapMin[INDEX_TWO] = ImageUtils::BytesToFloat(data, offset, size);
521 infoMain.gainMapMax[INDEX_TWO] = ImageUtils::BytesToFloat(data, offset, size);
522 infoMain.gamma[INDEX_TWO] = ImageUtils::BytesToFloat(data, offset, size);
523 infoMain.baseSdrImageOffset[INDEX_TWO] = ImageUtils::BytesToFloat(data, offset, size);
524 infoMain.altHdrImageOffset[INDEX_TWO] = ImageUtils::BytesToFloat(data, offset, size);
525 } else {
526 infoMain.gainMapMin[INDEX_ONE] = infoMain.gainMapMin[INDEX_ZERO];
527 infoMain.gainMapMax[INDEX_ONE] = infoMain.gainMapMax[INDEX_ZERO];
528 infoMain.gamma[INDEX_ONE] = infoMain.gamma[INDEX_ZERO];
529 infoMain.baseSdrImageOffset[INDEX_ONE] = infoMain.baseSdrImageOffset[INDEX_ZERO];
530 infoMain.altHdrImageOffset[INDEX_ONE] = infoMain.altHdrImageOffset[INDEX_ZERO];
531 infoMain.gainMapMin[INDEX_TWO] = infoMain.gainMapMin[INDEX_ZERO];
532 infoMain.gainMapMax[INDEX_TWO] = infoMain.gainMapMax[INDEX_ZERO];
533 infoMain.gamma[INDEX_TWO] = infoMain.gamma[INDEX_ZERO];
534 infoMain.baseSdrImageOffset[INDEX_TWO] = infoMain.baseSdrImageOffset[INDEX_ZERO];
535 infoMain.altHdrImageOffset[INDEX_TWO] = infoMain.altHdrImageOffset[INDEX_ZERO];
536 }
537 return infoMain;
538 }
539
ParseColorInfo(const uint8_t * data,uint32_t & offset,uint32_t length,ColorInfo & colorInfo)540 static bool ParseColorInfo(const uint8_t* data, uint32_t& offset, uint32_t length, ColorInfo& colorInfo)
541 {
542 uint8_t size = data[offset++];
543 bool cond = size == EMPTY_SIZE;
544 CHECK_ERROR_RETURN_RET(cond, true);
545 cond = size > length - offset || size != COLOR_INFO_BYTES;
546 CHECK_ERROR_RETURN_RET(cond, false);
547 colorInfo.primary = data[offset++];
548 colorInfo.transFunc = data[offset++];
549 colorInfo.model = data[offset++];
550 return true;
551 }
552
ParseTransformInfo(uint8_t * data,uint32_t & offset,uint32_t length,TransformInfo & info)553 static bool ParseTransformInfo(uint8_t* data, uint32_t& offset, uint32_t length, TransformInfo& info)
554 {
555 uint8_t size = ImageUtils::BytesToUint16(data, offset, length);
556 if (size == EMPTY_SIZE) {
557 info.mappingFlag = EMPTY_SIZE;
558 info.mapping.resize(EMPTY_SIZE);
559 return true;
560 }
561 if (size > length - offset || size <= UINT8_BYTE_COUNT) {
562 info.mappingFlag = EMPTY_SIZE;
563 info.mapping.resize(EMPTY_SIZE);
564 return false;
565 }
566 info.mappingFlag = data[offset++];
567 if (info.mappingFlag == INDEX_ZERO) {
568 info.mapping.resize(EMPTY_SIZE);
569 return true;
570 } else if (info.mappingFlag > size - UINT16_BYTE_COUNT) {
571 return false;
572 }
573 info.mapping.resize(info.mappingFlag);
574 if (memcpy_s(info.mapping.data(), info.mappingFlag, data + offset, info.mappingFlag) != EOK) {
575 return false;
576 }
577 offset += info.mappingFlag;
578 return true;
579 }
580
ConvertExtendInfoMain(ExtendInfoMain info,HDRVividExtendMetadata & metadata)581 static void ConvertExtendInfoMain(ExtendInfoMain info, HDRVividExtendMetadata& metadata)
582 {
583 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ZERO] = info.gainMapMax[INDEX_ZERO];
584 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ONE] = info.gainMapMax[INDEX_ONE];
585 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_TWO] = info.gainMapMax[INDEX_TWO];
586 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ZERO] = info.gainMapMin[INDEX_ZERO];
587 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ONE] = info.gainMapMin[INDEX_ONE];
588 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_TWO] = info.gainMapMin[INDEX_TWO];
589 metadata.metaISO.enhanceMappingGamma[INDEX_ZERO] = info.gamma[INDEX_ZERO];
590 metadata.metaISO.enhanceMappingGamma[INDEX_ONE] = info.gamma[INDEX_ONE];
591 metadata.metaISO.enhanceMappingGamma[INDEX_TWO] = info.gamma[INDEX_TWO];
592 metadata.metaISO.enhanceMappingBaselineOffset[INDEX_ZERO] = info.baseSdrImageOffset[INDEX_ZERO];
593 metadata.metaISO.enhanceMappingBaselineOffset[INDEX_ONE] = info.baseSdrImageOffset[INDEX_ONE];
594 metadata.metaISO.enhanceMappingBaselineOffset[INDEX_TWO] = info.baseSdrImageOffset[INDEX_TWO];
595 metadata.metaISO.enhanceMappingAlternateOffset[INDEX_ZERO] = info.altHdrImageOffset[INDEX_ZERO];
596 metadata.metaISO.enhanceMappingAlternateOffset[INDEX_ONE] = info.altHdrImageOffset[INDEX_ONE];
597 metadata.metaISO.enhanceMappingAlternateOffset[INDEX_TWO] = info.altHdrImageOffset[INDEX_TWO];
598 const float eps = 1e-5;
599 if ((fabs(metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ZERO] -
600 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ONE]) < eps) &&
601 (fabs(metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ZERO] -
602 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_TWO]) < eps) &&
603 (fabs(metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ZERO] -
604 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ONE]) < eps) &&
605 (fabs(metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ZERO] -
606 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_TWO]) < eps)) {
607 IMAGE_LOGI("HDR-IMAGE ParseISO enhanceClipped ThreholMaxGainmap has same num");
608 }
609 }
610
ConvertExtendInfoExtention(ExtendInfoExtention ext,HDRVividExtendMetadata & metadata)611 static void ConvertExtendInfoExtention(ExtendInfoExtention ext, HDRVividExtendMetadata& metadata)
612 {
613 metadata.baseColorMeta.baseColorPrimary = ext.baseColor.primary;
614 metadata.baseColorMeta.baseTransFunction = ext.baseColor.transFunc;
615 metadata.baseColorMeta.baseColorModel = ext.baseColor.model;
616 metadata.gainmapColorMeta.enhanceDataColorPrimary = ext.enhanceDataColor.primary;
617 metadata.gainmapColorMeta.enhanceDataTransFunction = ext.enhanceDataColor.transFunc;
618 metadata.gainmapColorMeta.enhanceDataColorModel = ext.enhanceDataColor.model;
619 metadata.gainmapColorMeta.combineColorPrimary = ext.combineColor.primary;
620 metadata.gainmapColorMeta.combineTransFunction = ext.combineColor.transFunc;
621 metadata.gainmapColorMeta.combineColorModel = ext.combineColor.model;
622 metadata.gainmapColorMeta.alternateColorPrimary = ext.enhanceColor.primary;
623 metadata.gainmapColorMeta.alternateTransFunction = ext.enhanceColor.transFunc;
624 metadata.gainmapColorMeta.alternateColorModel = ext.enhanceColor.model;
625 metadata.baseColorMeta.baseMappingFlag = ext.baseMapping.mappingFlag;
626 metadata.baseColorMeta.baseMapping = ext.baseMapping.mapping;
627 metadata.baseColorMeta.baseMappingSize = ext.baseMapping.mapping.size();
628 metadata.gainmapColorMeta.combineMappingFlag = ext.combineMapping.mappingFlag;
629 metadata.gainmapColorMeta.combineMapping = ext.combineMapping.mapping;
630 metadata.gainmapColorMeta.combineMappingSize = ext.combineMapping.mapping.size();
631 metadata.baseColorMeta.baseIccSize = EMPTY_SIZE;
632 metadata.baseColorMeta.baseICC.resize(EMPTY_SIZE);
633 metadata.gainmapColorMeta.enhanceICCSize = EMPTY_SIZE;
634 metadata.gainmapColorMeta.enhanceICC.resize(EMPTY_SIZE);
635 }
636
ParseVividJpegMetadata(uint8_t * data,uint32_t & dataOffset,uint32_t length,HdrMetadata & metadata)637 static bool ParseVividJpegMetadata(uint8_t* data, uint32_t& dataOffset, uint32_t length, HdrMetadata& metadata)
638 {
639 uint16_t metadataSize = ImageUtils::BytesToUint16(data, dataOffset, length);
640 CHECK_ERROR_RETURN_RET(metadataSize > length - dataOffset, false);
641 bool cond = !ParseVividJpegStaticMetadata(data, dataOffset, metadataSize, metadata.staticMetadata);
642 CHECK_ERROR_RETURN_RET(cond, false);
643 uint16_t dynamicMetaSize = ImageUtils::BytesToUint16(data, dataOffset, length);
644 if (dynamicMetaSize > 0) {
645 cond = dynamicMetaSize > length - dataOffset;
646 CHECK_ERROR_RETURN_RET(cond, false);
647 metadata.dynamicMetadata.resize(dynamicMetaSize);
648 cond = memcpy_s(metadata.dynamicMetadata.data(), dynamicMetaSize, data + dataOffset, dynamicMetaSize) != 0;
649 CHECK_DEBUG_RETURN_RET_LOG(cond, false, "get vivid dynamic metadata failed");
650 dataOffset += dynamicMetaSize;
651 }
652 return true;
653 }
654
ParseVividJpegExtendInfo(uint8_t * data,uint32_t length,HDRVividExtendMetadata & metadata)655 static bool ParseVividJpegExtendInfo(uint8_t* data, uint32_t length, HDRVividExtendMetadata& metadata)
656 {
657 uint32_t dataOffset = 0;
658 uint16_t metadataSize = ImageUtils::BytesToUint16(data, dataOffset, length);
659 CHECK_ERROR_RETURN_RET(metadataSize > length - UINT16_BYTE_COUNT, false);
660 uint8_t components = data[dataOffset++];
661 bool cond = components != THREE_COMPONENTS && components != ONE_COMPONENT;
662 CHECK_ERROR_RETURN_RET(cond, false);
663 ExtendInfoMain extendInfoMain = ParseExtendInfoMain(data, dataOffset, length, components == THREE_COMPONENTS);
664 ExtendInfoExtention extendInfoExtention;
665 cond = !ParseColorInfo(data, dataOffset, length, extendInfoExtention.baseColor);
666 CHECK_ERROR_RETURN_RET(cond, false);
667 cond = !ParseColorInfo(data, dataOffset, length, extendInfoExtention.enhanceDataColor);
668 CHECK_ERROR_RETURN_RET(cond, false);
669 cond = !ParseColorInfo(data, dataOffset, length, extendInfoExtention.enhanceColor);
670 CHECK_ERROR_RETURN_RET(cond, false);
671 cond = !ParseColorInfo(data, dataOffset, length, extendInfoExtention.combineColor);
672 CHECK_ERROR_RETURN_RET(cond, false);
673 cond = !ParseTransformInfo(data, dataOffset, length, extendInfoExtention.baseMapping);
674 CHECK_ERROR_RETURN_RET(cond, false);
675 cond = !ParseTransformInfo(data, dataOffset, length, extendInfoExtention.combineMapping);
676 CHECK_ERROR_RETURN_RET(cond, false);
677 ConvertExtendInfoMain(extendInfoMain, metadata);
678 ConvertExtendInfoExtention(extendInfoExtention, metadata);
679 return true;
680 }
681
ParseVividMetadata(uint8_t * data,uint32_t length,HdrMetadata & metadata)682 static bool ParseVividMetadata(uint8_t* data, uint32_t length, HdrMetadata& metadata)
683 {
684 uint32_t dataOffset = 0;
685 uint8_t itut35CountryCode = data[dataOffset++];
686 uint16_t terminalProvideCode = ImageUtils::BytesToUint16(data, dataOffset, length);
687 uint16_t terminalProvideOrientedCode = ImageUtils::BytesToUint16(data, dataOffset, length);
688 IMAGE_LOGD("vivid metadata countryCode=%{public}d,terminalCode=%{public}d,orientedcode=%{public}d",
689 itut35CountryCode, terminalProvideCode, terminalProvideOrientedCode);
690 uint8_t extendedFrameNumber = data[dataOffset++];
691 bool cond = extendedFrameNumber < JPEG_IMAGE_NUM;
692 CHECK_ERROR_RETURN_RET(cond, false);
693 uint8_t fileType = data[dataOffset++];
694 uint8_t metaType = data[dataOffset++];
695 uint8_t enhanceType = data[dataOffset++];
696 uint8_t hdrType = data[dataOffset++];
697 IMAGE_LOGD("vivid metadata info hdrType=%{public}d, enhanceType=%{public}d", hdrType, enhanceType);
698 cond = fileType != VIVID_FILE_TYPE_GAINMAP;
699 CHECK_ERROR_RETURN_RET(cond, false);
700 bool getMetadata = false;
701 if (metaType > 0 && length > dataOffset + UINT16_BYTE_COUNT) {
702 getMetadata = ParseVividJpegMetadata(data, dataOffset, length, metadata);
703 }
704 const uint8_t hasEnhanceInfo = 1;
705 if (enhanceType == hasEnhanceInfo && length > dataOffset + UINT16_BYTE_COUNT) {
706 metadata.extendMetaFlag =
707 ParseVividJpegExtendInfo(data + dataOffset, length - dataOffset, metadata.extendMeta);
708 IMAGE_LOGD("vivid get extend info result = %{public}d", metadata.extendMetaFlag);
709 } else {
710 metadata.extendMetaFlag = false;
711 }
712 return getMetadata;
713 }
714
GetVividJpegMetadata(jpeg_marker_struct * markerList,HdrMetadata & metadata)715 static bool GetVividJpegMetadata(jpeg_marker_struct* markerList, HdrMetadata& metadata)
716 {
717 for (jpeg_marker_struct* marker = markerList; marker; marker = marker->next) {
718 if (JPEG_MARKER_APP8 != marker->marker) {
719 continue;
720 }
721 if (memcmp(marker->data, ITUT35_TAG, ITUT35_TAG_SIZE) != 0) {
722 continue;
723 }
724 uint8_t* data = marker->data + ITUT35_TAG_SIZE;
725 uint32_t length = marker->data_length - ITUT35_TAG_SIZE;
726 return ParseVividMetadata(data, length, metadata);
727 }
728 return false;
729 }
730
ParseISOExtendInfoMain(uint8_t * data,uint32_t & offset,uint32_t length,ExtendInfoMain & info,uint8_t index)731 static void ParseISOExtendInfoMain(uint8_t* data, uint32_t& offset, uint32_t length,
732 ExtendInfoMain& info, uint8_t index)
733 {
734 bool cond = index > INDEX_TWO;
735 CHECK_ERROR_RETURN(cond);
736 int32_t minGainmapNumerator = ImageUtils::BytesToInt32(data, offset, length);
737 uint32_t minGainmapDenominator = ImageUtils::BytesToUint32(data, offset, length);
738 int32_t maxGainmapNumerator = ImageUtils::BytesToInt32(data, offset, length);
739 uint32_t maxGainmapDenominator = ImageUtils::BytesToUint32(data, offset, length);
740 uint32_t gammaNumerator = ImageUtils::BytesToUint32(data, offset, length);
741 uint32_t gammaDenominator = ImageUtils::BytesToUint32(data, offset, length);
742 int32_t baseImageOffsetNumerator = ImageUtils::BytesToInt32(data, offset, length);
743 uint32_t baseImageOffsetDenominator = ImageUtils::BytesToUint32(data, offset, length);
744 int32_t altImageOffsetNumerator = ImageUtils::BytesToInt32(data, offset, length);
745 uint32_t altImageOffsetDenominator = ImageUtils::BytesToUint32(data, offset, length);
746 IMAGE_LOGD("HDR-IMAGE ParseISO minGainmapNumerator %{public}d", minGainmapNumerator);
747 IMAGE_LOGD("HDR-IMAGE ParseISO maxGainmapNumerator %{public}d", maxGainmapNumerator);
748 IMAGE_LOGD("HDR-IMAGE ParseISO gammaNumerator %{public}d", gammaNumerator);
749 IMAGE_LOGD("HDR-IMAGE ParseISO baseImageOffsetNumerator %{public}d", baseImageOffsetNumerator);
750 IMAGE_LOGD("HDR-IMAGE ParseISO altImageOffsetNumerator %{public}d", altImageOffsetNumerator);
751 if (minGainmapDenominator == EMPTY_SIZE) {
752 info.gainMapMin[index] = EMPTY_SIZE;
753 } else {
754 info.gainMapMin[index] = (float)minGainmapNumerator / (float)minGainmapDenominator;
755 }
756
757 if (maxGainmapDenominator == EMPTY_SIZE) {
758 info.gainMapMax[index] = EMPTY_SIZE;
759 } else {
760 info.gainMapMax[index] = (float)maxGainmapNumerator / (float)maxGainmapDenominator;
761 }
762
763 if (gammaDenominator == EMPTY_SIZE) {
764 info.gamma[index] = EMPTY_SIZE;
765 } else {
766 info.gamma[index] = (float)gammaNumerator / (float)gammaDenominator;
767 }
768
769 if (baseImageOffsetDenominator == EMPTY_SIZE) {
770 info.baseSdrImageOffset[index] = EMPTY_SIZE;
771 } else {
772 info.baseSdrImageOffset[index] = (float)baseImageOffsetNumerator / (float)baseImageOffsetDenominator;
773 }
774
775 if (altImageOffsetDenominator == EMPTY_SIZE) {
776 info.altHdrImageOffset[index] = EMPTY_SIZE;
777 } else {
778 info.altHdrImageOffset[index] = (float)altImageOffsetNumerator / (float)altImageOffsetDenominator;
779 }
780 }
781
ParseISOExtendInfoThreeCom(uint8_t * data,uint32_t & offset,uint32_t lenght,uint8_t channelNum,ExtendInfoMain & info)782 static void ParseISOExtendInfoThreeCom(uint8_t* data, uint32_t& offset, uint32_t lenght,
783 uint8_t channelNum, ExtendInfoMain& info)
784 {
785 ParseISOExtendInfoMain(data, offset, lenght, info, INDEX_ZERO);
786 if (channelNum == THREE_COMPONENTS) {
787 ParseISOExtendInfoMain(data, offset, lenght, info, INDEX_ONE);
788 ParseISOExtendInfoMain(data, offset, lenght, info, INDEX_TWO);
789 } else {
790 info.gainMapMin[INDEX_ONE] = info.gainMapMin[INDEX_ZERO];
791 info.gainMapMax[INDEX_ONE] = info.gainMapMax[INDEX_ZERO];
792 info.gamma[INDEX_ONE] = info.gamma[INDEX_ZERO];
793 info.baseSdrImageOffset[INDEX_ONE] = info.baseSdrImageOffset[INDEX_ZERO];
794 info.altHdrImageOffset[INDEX_ONE] = info.altHdrImageOffset[INDEX_ZERO];
795 info.gainMapMin[INDEX_TWO] = info.gainMapMin[INDEX_ZERO];
796 info.gainMapMax[INDEX_TWO] = info.gainMapMax[INDEX_ZERO];
797 info.gamma[INDEX_TWO] = info.gamma[INDEX_ZERO];
798 info.baseSdrImageOffset[INDEX_TWO] = info.baseSdrImageOffset[INDEX_ZERO];
799 info.altHdrImageOffset[INDEX_TWO] = info.altHdrImageOffset[INDEX_ZERO];
800 }
801 }
802
ParseISOMetadata(uint8_t * data,uint32_t length,HdrMetadata & metadata)803 static bool ParseISOMetadata(uint8_t* data, uint32_t length, HdrMetadata& metadata)
804 {
805 uint32_t dataOffset = 0;
806 bool cond = length < ISO_GAINMAP_METADATA_PAYLOAD_MIN_SIZE;
807 CHECK_ERROR_RETURN_RET(cond, false);
808 metadata.extendMeta.metaISO.writeVersion = ImageUtils::BytesToUint32(data, dataOffset, length);
809 metadata.extendMeta.metaISO.miniVersion = metadata.extendMeta.metaISO.writeVersion & 0xFF;
810 cond = metadata.extendMeta.metaISO.miniVersion != EMPTY_SIZE;
811 CHECK_ERROR_RETURN_RET(cond, false);
812 uint8_t flag = data[dataOffset++];
813 metadata.extendMeta.metaISO.gainmapChannelNum = ((flag & 0x80) == 0x80) ? THREE_COMPONENTS : ONE_COMPONENT;
814 metadata.extendMeta.metaISO.useBaseColorFlag = ((flag & 0x40) == 0x40) ? 0x01 : 0x00;
815
816 uint32_t baseHeadroomNumerator = ImageUtils::BytesToUint32(data, dataOffset, length);
817 uint32_t baseHeadroomDenominator = ImageUtils::BytesToUint32(data, dataOffset, length);
818 uint32_t altHeadroomNumerator = ImageUtils::BytesToUint32(data, dataOffset, length);
819 uint32_t altHeadroomDenominator = ImageUtils::BytesToUint32(data, dataOffset, length);
820
821 if (baseHeadroomDenominator != EMPTY_SIZE) {
822 metadata.extendMeta.metaISO.baseHeadroom = (float)baseHeadroomNumerator / (float)baseHeadroomDenominator;
823 } else {
824 metadata.extendMeta.metaISO.baseHeadroom = (float)EMPTY_SIZE;
825 }
826 if (altHeadroomDenominator != EMPTY_SIZE) {
827 metadata.extendMeta.metaISO.alternateHeadroom = (float)altHeadroomNumerator / (float)altHeadroomDenominator;
828 } else {
829 metadata.extendMeta.metaISO.alternateHeadroom = (float)EMPTY_SIZE;
830 }
831 ExtendInfoMain infoMain{};
832 ParseISOExtendInfoThreeCom(data, dataOffset, length, metadata.extendMeta.metaISO.gainmapChannelNum, infoMain);
833 ConvertExtendInfoMain(infoMain, metadata.extendMeta);
834 metadata.extendMetaFlag = true;
835 return true;
836 }
837
GetISOGainmapMetadata(jpeg_marker_struct * markerList,HdrMetadata & metadata)838 static bool GetISOGainmapMetadata(jpeg_marker_struct* markerList, HdrMetadata& metadata)
839 {
840 for (jpeg_marker_struct* marker = markerList; marker; marker = marker->next) {
841 if (JPEG_MARKER_APP2 != marker->marker) {
842 continue;
843 }
844 if (marker->data_length <= ISO_GAINMAP_TAG_SIZE ||
845 memcmp(marker->data, ISO_GAINMAP_TAG, ISO_GAINMAP_TAG_SIZE) != 0) {
846 continue;
847 }
848 uint8_t* data = marker->data + ISO_GAINMAP_TAG_SIZE;
849 uint32_t length = marker->data_length - ISO_GAINMAP_TAG_SIZE;
850 return ParseISOMetadata(data, length, metadata);
851 }
852 return false;
853 }
854
GetHdrMediaTypeInfo(jpeg_marker_struct * markerList,HdrMetadata & metadata)855 static bool GetHdrMediaTypeInfo(jpeg_marker_struct* markerList, HdrMetadata& metadata)
856 {
857 for (jpeg_marker_struct* marker = markerList; marker; marker = marker->next) {
858 if (JPEG_MARKER_APP11 != marker->marker) {
859 continue;
860 }
861 if (marker->data_length <= HDR_MEDIA_TYPE_TAG_SIZE ||
862 memcmp(marker->data, HDR_MEDIA_TYPE_TAG, HDR_MEDIA_TYPE_TAG_SIZE) != 0) {
863 continue;
864 }
865 uint8_t* data = marker->data + HDR_MEDIA_TYPE_TAG_SIZE;
866 uint32_t dataOffset = 0;
867 uint32_t hdrMediaType = ImageUtils::BytesToUint32(data, dataOffset, marker->data_length);
868 metadata.hdrMetadataType = static_cast<int32_t>(hdrMediaType);
869 return true;
870 }
871 return false;
872 }
873
GetJpegGainMapMetadata(SkJpegCodec * codec,ImageHdrType type,HdrMetadata & metadata)874 static bool GetJpegGainMapMetadata(SkJpegCodec* codec, ImageHdrType type, HdrMetadata& metadata)
875 {
876 bool cond = codec == nullptr || codec->decoderMgr() == nullptr;
877 CHECK_ERROR_RETURN_RET_LOG(cond, false, "GetJpegGainMapMetadata codec is nullptr");
878 jpeg_marker_struct* markerList = codec->decoderMgr()->dinfo()->marker_list;
879 CHECK_ERROR_RETURN_RET(!markerList, false);
880 GetHdrMediaTypeInfo(markerList, metadata);
881 switch (type) {
882 case ImageHdrType::HDR_VIVID_DUAL: {
883 bool res = GetVividJpegMetadata(markerList, metadata);
884 if (res && !metadata.extendMetaFlag) {
885 GetISOGainmapMetadata(markerList, metadata);
886 }
887 return res;
888 }
889 case ImageHdrType::HDR_CUVA:
890 return true;
891 case ImageHdrType::HDR_ISO_DUAL:
892 return GetISOGainmapMetadata(markerList, metadata);
893 default:
894 return false;
895 }
896 }
897
898 #ifdef HEIF_HW_DECODE_ENABLE
ParseHeifStaticMetadata(const vector<uint8_t> & displayInfo,const vector<uint8_t> & lightInfo)899 static vector<uint8_t> ParseHeifStaticMetadata(const vector<uint8_t>& displayInfo, const vector<uint8_t>& lightInfo)
900 {
901 #if defined(_WIN32) || defined(_APPLE) || defined(IOS_PLATFORM) || defined(ANDROID_PLATFORM)
902 return {};
903 #else
904 HDI::Display::Graphic::Common::V1_0::HdrStaticMetadata staticMetadata{};
905 DisplayColourVolume displayColourVolume{};
906 ContentLightLevelInfo lightLevelInfo{};
907 if (!lightInfo.empty()) {
908 if (memcpy_s(&lightLevelInfo, sizeof(ContentLightLevelInfo), lightInfo.data(), lightInfo.size()) != EOK) {
909 return {};
910 }
911 }
912 if (!displayInfo.empty()) {
913 if (memcpy_s(&displayColourVolume, sizeof(DisplayColourVolume),
914 displayInfo.data(), displayInfo.size()) != EOK) {
915 return {};
916 }
917 }
918 const float colorScale = 0.00002f;
919 const float lumScale = 0.0001f;
920 staticMetadata.smpte2086.displayPrimaryRed.x = colorScale * (float)displayColourVolume.red.x;
921 staticMetadata.smpte2086.displayPrimaryRed.y = colorScale * (float)displayColourVolume.red.y;
922 staticMetadata.smpte2086.displayPrimaryGreen.x = colorScale * (float)displayColourVolume.green.x;
923 staticMetadata.smpte2086.displayPrimaryGreen.y = colorScale * (float)displayColourVolume.green.y;
924 staticMetadata.smpte2086.displayPrimaryBlue.x = colorScale * (float)displayColourVolume.blue.x;
925 staticMetadata.smpte2086.displayPrimaryBlue.y = colorScale * (float)displayColourVolume.blue.y;
926 staticMetadata.smpte2086.whitePoint.x = colorScale * (float)displayColourVolume.whitePoint.x;
927 staticMetadata.smpte2086.whitePoint.y = colorScale * (float)displayColourVolume.whitePoint.y;
928 staticMetadata.smpte2086.maxLuminance = (float)displayColourVolume.luminanceMax;
929 staticMetadata.smpte2086.minLuminance = lumScale * (float)displayColourVolume.luminanceMin;
930 staticMetadata.cta861.maxContentLightLevel = (float)lightLevelInfo.maxContentLightLevel;
931 staticMetadata.cta861.maxFrameAverageLightLevel = (float)lightLevelInfo.maxPicAverageLightLevel;
932 uint32_t vecSize = sizeof(HDI::Display::Graphic::Common::V1_0::HdrStaticMetadata);
933 std::vector<uint8_t> staticMetadataVec(vecSize);
934 if (memcpy_s(staticMetadataVec.data(), vecSize, &staticMetadata, vecSize) != EOK) {
935 return {};
936 }
937 return staticMetadataVec;
938 #endif
939 }
940
GetHeifMetadata(HeifDecoder * heifDecoder,ImageHdrType type,HdrMetadata & metadata)941 static bool GetHeifMetadata(HeifDecoder* heifDecoder, ImageHdrType type, HdrMetadata& metadata)
942 {
943 bool cond = heifDecoder == nullptr;
944 CHECK_ERROR_RETURN_RET(cond, false);
945 if (type == ImageHdrType::HDR_VIVID_DUAL || type == ImageHdrType::HDR_VIVID_SINGLE) {
946 vector<uint8_t> uwaInfo;
947 vector<uint8_t> displayInfo;
948 vector<uint8_t> lightInfo;
949 heifDecoder->getVividMetadata(uwaInfo, displayInfo, lightInfo);
950 cond = uwaInfo.empty();
951 CHECK_ERROR_RETURN_RET(cond, false);
952 metadata.staticMetadata = ParseHeifStaticMetadata(displayInfo, lightInfo);
953 bool res = ParseVividMetadata(uwaInfo.data(), uwaInfo.size(), metadata);
954 if (!metadata.extendMetaFlag) {
955 vector<uint8_t> isoMetadata;
956 heifDecoder->getISOMetadata(isoMetadata);
957 cond = isoMetadata.empty();
958 CHECK_ERROR_RETURN_RET(cond, res);
959 if (isoMetadata.size() > EMPTY_SIZE && isoMetadata[INDEX_ZERO] == EMPTY_SIZE) {
960 ParseISOMetadata(isoMetadata.data() + INDEX_ONE, isoMetadata.size() - INDEX_ONE, metadata);
961 }
962 }
963 return res;
964 } else if (type == ImageHdrType::HDR_ISO_DUAL || type == ImageHdrType::HDR_ISO_SINGLE) {
965 vector<uint8_t> isoMetadata;
966 heifDecoder->getISOMetadata(isoMetadata);
967 if (isoMetadata.empty()) {
968 return false;
969 }
970 if (isoMetadata.size() > EMPTY_SIZE && isoMetadata[INDEX_ZERO] == EMPTY_SIZE) {
971 return ParseISOMetadata(isoMetadata.data() + INDEX_ONE, isoMetadata.size() - INDEX_ONE, metadata);
972 }
973 }
974 return false;
975 }
976 #endif
977
GetMetadata(SkCodec * codec,ImageHdrType type,HdrMetadata & metadata)978 bool HdrHelper::GetMetadata(SkCodec* codec, ImageHdrType type, HdrMetadata& metadata)
979 {
980 bool cond = type <= ImageHdrType::SDR || codec == nullptr;
981 CHECK_ERROR_RETURN_RET(cond, false);
982 switch (codec->getEncodedFormat()) {
983 case SkEncodedImageFormat::kJPEG: {
984 SkJpegCodec* jpegCodec = static_cast<SkJpegCodec*>(codec);
985 return GetJpegGainMapMetadata(jpegCodec, type, metadata);
986 }
987 case SkEncodedImageFormat::kHEIF: {
988 #ifdef HEIF_HW_DECODE_ENABLE
989 auto decoder = reinterpret_cast<HeifDecoder*>(codec->getHeifContext());
990 return GetHeifMetadata(decoder, type, metadata);
991 #else
992 return false;
993 #endif
994 }
995 default:
996 return false;
997 }
998 }
999
1000 /// pack jpeg base image vivid marker
PackVividPreInfo(vector<uint8_t> & bytes,uint32_t & offset,bool base,bool enhanceType)1001 static void PackVividPreInfo(vector<uint8_t>& bytes, uint32_t& offset, bool base, bool enhanceType)
1002 {
1003 bytes[offset++] = 0x26; // itut35 country code
1004 bytes[offset++] = 0x00;
1005 bytes[offset++] = 0x04; // terminalProvideCode
1006 bytes[offset++] = 0x00;
1007 bytes[offset++] = 0x07; // terminalProvideOrientedCode
1008 bytes[offset++] = 0x02; // extendFrameNumber
1009 bytes[offset++] = base ? INDEX_ONE : INDEX_TWO; // fileType
1010 bytes[offset++] = base ? INDEX_ZERO : INDEX_ONE; // metaType
1011 bytes[offset++] = ((!base) && enhanceType) ? INDEX_ONE : INDEX_ZERO; // enhanceType
1012 bytes[offset++] = INDEX_ZERO;
1013 }
1014
1015
GetBaseVividMarkerSize()1016 uint32_t HdrJpegPackerHelper::GetBaseVividMarkerSize()
1017 {
1018 const uint32_t baseInfoMarkerLength =
1019 JPEG_MARKER_TAG_SIZE + JPEG_MARKER_LENGTH_SIZE + ITUT35_TAG_SIZE +
1020 VIVID_PRE_INFO_SIZE + UINT32_BYTE_COUNT + // offset size
1021 UINT32_BYTE_COUNT; // gainmap length size
1022 return baseInfoMarkerLength;
1023 }
1024
GetMpfMarkerSize()1025 uint32_t HdrJpegPackerHelper::GetMpfMarkerSize()
1026 {
1027 return HDR_MULTI_PICTURE_APP_LENGTH;
1028 }
1029
PackBaseVividMarker(uint32_t gainmapOffset,uint32_t preOffset,uint32_t appSize)1030 vector<uint8_t> HdrJpegPackerHelper::PackBaseVividMarker(uint32_t gainmapOffset, uint32_t preOffset, uint32_t appSize)
1031 {
1032 const uint32_t baseInfoMarkerLength = GetBaseVividMarkerSize();
1033 vector<uint8_t> bytes(baseInfoMarkerLength);
1034 uint32_t index = 0;
1035 bytes[index++] = JPEG_MARKER_PREFIX;
1036 bytes[index++] = JPEG_MARKER_APP8;
1037 // length does not contain marker tag size;
1038 uint32_t markerDataLength = baseInfoMarkerLength - JPEG_MARKER_TAG_SIZE;
1039 ImageUtils::Uint16ToBytes(markerDataLength, bytes, index);
1040 ImageUtils::ArrayToBytes(ITUT35_TAG, ITUT35_TAG_SIZE, bytes, index);
1041 PackVividPreInfo(bytes, index, true, false);
1042 // set gainmap offset1 (gainmapOffset - current position offset)
1043 ImageUtils::Uint32ToBytes(gainmapOffset - preOffset, bytes, index);
1044 // set gainmap offset2 (gainmap size - app size)
1045 ImageUtils::Uint32ToBytes(gainmapOffset - appSize, bytes, index);
1046 return bytes;
1047 }
1048
PackBaseMpfMarker(uint32_t baseSize,uint32_t gainmapSize,uint32_t appOffset)1049 vector<uint8_t> HdrJpegPackerHelper::PackBaseMpfMarker(uint32_t baseSize, uint32_t gainmapSize, uint32_t appOffset)
1050 {
1051 SingleJpegImage baseImage = {
1052 .offset = 0,
1053 .size = baseSize,
1054 };
1055 SingleJpegImage gainmapImage = {
1056 .offset = baseSize - appOffset - JPEG_MARKER_TAG_SIZE - JPEG_MARKER_LENGTH_SIZE - MPF_TAG_SIZE,
1057 .size = gainmapSize,
1058 };
1059 return JpegMpfPacker::PackHdrJpegMpfMarker(baseImage, gainmapImage);
1060 }
1061
PackBaseISOMarker()1062 vector<uint8_t> HdrJpegPackerHelper::PackBaseISOMarker()
1063 {
1064 // marker + isoGainmapTag + ISOPayload
1065 uint32_t isoBaseLength = UINT32_BYTE_COUNT + ISO_GAINMAP_TAG_SIZE + UINT32_BYTE_COUNT;
1066 vector<uint8_t> bytes(isoBaseLength);
1067 uint32_t index = 0;
1068 bytes[index++] = JPEG_MARKER_PREFIX;
1069 bytes[index++] = JPEG_MARKER_APP2;
1070 // length dose not contain marker
1071 uint32_t length = isoBaseLength - JPEG_MARKER_TAG_SIZE;
1072 ImageUtils::Uint16ToBytes(length, bytes, index); // set iso marker size
1073 ImageUtils::ArrayToBytes(ISO_GAINMAP_TAG, ISO_GAINMAP_TAG_SIZE, bytes, index);
1074 ImageUtils::Uint32ToBytes(EMPTY_SIZE, bytes, index); // set iso payload size
1075 return bytes;
1076 }
1077
PackHdrMediaTypeMarker(HdrMetadata & hdrMetadata)1078 vector<uint8_t> HdrJpegPackerHelper::PackHdrMediaTypeMarker(HdrMetadata& hdrMetadata)
1079 {
1080 // marker + hdrMediaTypeTag + TypeNumber
1081 uint32_t hdrMediaTypeLength = UINT32_BYTE_COUNT + HDR_MEDIA_TYPE_TAG_SIZE + UINT32_BYTE_COUNT;
1082 vector<uint8_t> bytes(hdrMediaTypeLength);
1083 uint32_t index = 0;
1084 bytes[index++] = JPEG_MARKER_PREFIX;
1085 bytes[index++] = JPEG_MARKER_APP11;
1086 // length dose not contain marker
1087 uint32_t length = hdrMediaTypeLength - JPEG_MARKER_TAG_SIZE;
1088 ImageUtils::Uint16ToBytes(length, bytes, index); // set iso marker size
1089 ImageUtils::ArrayToBytes(HDR_MEDIA_TYPE_TAG, HDR_MEDIA_TYPE_TAG_SIZE, bytes, index);
1090 ImageUtils::Uint32ToBytes(static_cast<uint32_t>(hdrMetadata.hdrMetadataType), bytes, index);
1091 return bytes;
1092 }
1093
PackExtendInfoMain(vector<uint8_t> & bytes,uint32_t & offset,HDRVividExtendMetadata & metadata)1094 static void PackExtendInfoMain(vector<uint8_t>& bytes, uint32_t& offset, HDRVividExtendMetadata& metadata)
1095 {
1096 IMAGE_LOGI("HDR-IMAGE PackISO MinGainmap = %{public}f, MaxGainmap = %{public}f,"
1097 "BaselineOffset = %{public}f, AlternateOffset = %{public}f",
1098 metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ZERO],
1099 metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ZERO],
1100 metadata.metaISO.enhanceMappingBaselineOffset[INDEX_ZERO],
1101 metadata.metaISO.enhanceMappingAlternateOffset[INDEX_ZERO]);
1102 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ZERO], bytes, offset);
1103 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ZERO], bytes, offset);
1104 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingGamma[INDEX_ZERO], bytes, offset);
1105 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingBaselineOffset[INDEX_ZERO], bytes, offset);
1106 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingAlternateOffset[INDEX_ZERO], bytes, offset);
1107
1108 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_ONE], bytes, offset);
1109 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_ONE], bytes, offset);
1110 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingGamma[INDEX_ONE], bytes, offset);
1111 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingBaselineOffset[INDEX_ONE], bytes, offset);
1112 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingAlternateOffset[INDEX_ONE], bytes, offset);
1113
1114 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMinGainmap[INDEX_TWO], bytes, offset);
1115 ImageUtils::FloatToBytes(metadata.metaISO.enhanceClippedThreholdMaxGainmap[INDEX_TWO], bytes, offset);
1116 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingGamma[INDEX_TWO], bytes, offset);
1117 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingBaselineOffset[INDEX_TWO], bytes, offset);
1118 ImageUtils::FloatToBytes(metadata.metaISO.enhanceMappingAlternateOffset[INDEX_TWO], bytes, offset);
1119 }
1120
PackTransformInfo(vector<uint8_t> & bytes,uint32_t & offset,uint8_t flag,vector<uint8_t> mapping)1121 static void PackTransformInfo(vector<uint8_t>& bytes, uint32_t& offset, uint8_t flag, vector<uint8_t> mapping)
1122 {
1123 if (flag == EMPTY_SIZE || flag != mapping.size()) {
1124 ImageUtils::Uint16ToBytes((uint16_t)EMPTY_SIZE, bytes, offset);
1125 return;
1126 }
1127 uint16_t transformSize = flag + UINT8_BYTE_COUNT;
1128 ImageUtils::Uint16ToBytes(transformSize, bytes, offset);
1129 if (offset + flag > bytes.size()) {
1130 return;
1131 }
1132 bytes[offset++] = flag;
1133 size_t remainSpace = bytes.size() - offset;
1134 if (remainSpace < flag) {
1135 offset -= (UINT8_BYTE_COUNT + UINT16_BYTE_COUNT);
1136 ImageUtils::Uint16ToBytes((uint16_t)EMPTY_SIZE, bytes, offset);
1137 return;
1138 }
1139 if (memcpy_s(bytes.data() + offset, flag, mapping.data(), flag) != 0) {
1140 offset -= (UINT8_BYTE_COUNT + UINT16_BYTE_COUNT);
1141 ImageUtils::Uint16ToBytes((uint16_t)EMPTY_SIZE, bytes, offset);
1142 return;
1143 }
1144 offset += flag;
1145 }
1146
PackExtendInfoExtention(vector<uint8_t> & bytes,uint32_t & offset,const HDRVividExtendMetadata & metadata)1147 static void PackExtendInfoExtention(vector<uint8_t>& bytes, uint32_t& offset, const HDRVividExtendMetadata& metadata)
1148 {
1149 bytes[offset++] = COLOR_INFO_BYTES;
1150 bytes[offset++] = metadata.baseColorMeta.baseColorPrimary;
1151 bytes[offset++] = metadata.baseColorMeta.baseTransFunction;
1152 bytes[offset++] = metadata.baseColorMeta.baseColorModel;
1153 bytes[offset++] = COLOR_INFO_BYTES;
1154 bytes[offset++] = metadata.gainmapColorMeta.enhanceDataColorPrimary;
1155 bytes[offset++] = metadata.gainmapColorMeta.enhanceDataTransFunction;
1156 bytes[offset++] = metadata.gainmapColorMeta.enhanceDataColorModel;
1157 bytes[offset++] = COLOR_INFO_BYTES;
1158 bytes[offset++] = metadata.gainmapColorMeta.alternateColorPrimary;
1159 bytes[offset++] = metadata.gainmapColorMeta.alternateTransFunction;
1160 bytes[offset++] = metadata.gainmapColorMeta.alternateColorModel;
1161 bytes[offset++] = COLOR_INFO_BYTES;
1162 bytes[offset++] = metadata.gainmapColorMeta.combineColorPrimary;
1163 bytes[offset++] = metadata.gainmapColorMeta.combineTransFunction;
1164 bytes[offset++] = metadata.gainmapColorMeta.combineColorModel;
1165 PackTransformInfo(bytes, offset, metadata.baseColorMeta.baseMappingFlag, metadata.baseColorMeta.baseMapping);
1166 PackTransformInfo(bytes, offset, metadata.gainmapColorMeta.combineMappingFlag,
1167 metadata.gainmapColorMeta.combineMapping);
1168 }
1169
GetExtendMetadataSize(bool vividExtendFlag,const HDRVividExtendMetadata & metadata)1170 static uint16_t GetExtendMetadataSize(bool vividExtendFlag, const HDRVividExtendMetadata& metadata)
1171 {
1172 if (!vividExtendFlag) {
1173 return EMPTY_SIZE;
1174 }
1175 const uint8_t colorInfoNum = 4;
1176 uint16_t extendInfoExtentionSize = (COLOR_INFO_BYTES + UINT8_BYTE_COUNT) * colorInfoNum +
1177 UINT16_BYTE_COUNT + UINT16_BYTE_COUNT; // "baseTransformInfoSize" count + "EnhanceTransformInfoSize" count
1178 if (metadata.baseColorMeta.baseMappingFlag > EMPTY_SIZE) {
1179 // "mapping" + "mapping" bytes
1180 extendInfoExtentionSize += metadata.baseColorMeta.baseMappingFlag + UINT8_BYTE_COUNT;
1181 }
1182 if (metadata.gainmapColorMeta.combineMappingFlag > EMPTY_SIZE) {
1183 extendInfoExtentionSize += metadata.gainmapColorMeta.combineMappingFlag + UINT8_BYTE_COUNT;
1184 }
1185 uint16_t extendSize = UINT8_BYTE_COUNT + EXTEND_INFO_MAIN_SIZE + extendInfoExtentionSize;
1186 return extendSize;
1187 }
1188
PackExtendMetadata(vector<uint8_t> & bytes,uint32_t & index,HDRVividExtendMetadata & metadata)1189 static void PackExtendMetadata(vector<uint8_t>& bytes, uint32_t& index, HDRVividExtendMetadata& metadata)
1190 {
1191 uint16_t length = GetExtendMetadataSize(true, metadata);
1192 if (index + length > bytes.size()) {
1193 return;
1194 }
1195 ImageUtils::Uint16ToBytes(length, bytes, index);
1196 bytes[index++] = THREE_COMPONENTS;
1197 PackExtendInfoMain(bytes, index, metadata);
1198 PackExtendInfoExtention(bytes, index, metadata);
1199 }
1200
PackVividStaticMetadata(vector<uint8_t> & bytes,uint32_t & index,vector<uint8_t> & staticVec)1201 static bool PackVividStaticMetadata(vector<uint8_t>& bytes, uint32_t& index, vector<uint8_t>& staticVec)
1202 {
1203 #if defined(_WIN32) || defined(_APPLE) || defined(IOS_PLATFORM) || defined(ANDROID_PLATFORM)
1204 return false;
1205 #else
1206 HdrStaticMetadata staticMeta;
1207 uint32_t vecSize = sizeof(HdrStaticMetadata);
1208 bool cond = memcpy_s(&staticMeta, vecSize, staticVec.data(), vecSize) != EOK;
1209 CHECK_ERROR_RETURN_RET(cond, false);
1210 ImageUtils::Uint16ToBytes(VIVID_STATIC_METADATA_SIZE_IN_IMAGE, bytes, index);
1211 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryRed.x / SM_COLOR_SCALE), bytes, index);
1212 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryRed.y / SM_COLOR_SCALE), bytes, index);
1213 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryGreen.x / SM_COLOR_SCALE), bytes, index);
1214 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryGreen.y / SM_COLOR_SCALE), bytes, index);
1215 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryBlue.x / SM_COLOR_SCALE), bytes, index);
1216 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.displayPrimaryBlue.y / SM_COLOR_SCALE), bytes, index);
1217 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.whitePoint.x / SM_COLOR_SCALE), bytes, index);
1218 ImageUtils::Uint16ToBytes((uint16_t)(staticMeta.smpte2086.whitePoint.y / SM_COLOR_SCALE), bytes, index);
1219 ImageUtils::Uint32ToBytes((uint16_t)(staticMeta.smpte2086.maxLuminance), bytes, index);
1220 ImageUtils::Uint32ToBytes((uint16_t)(staticMeta.smpte2086.minLuminance / SM_LUM_SCALE), bytes, index);
1221 ImageUtils::Uint16ToBytes((uint16_t)staticMeta.cta861.maxContentLightLevel, bytes, index);
1222 ImageUtils::Uint16ToBytes((uint16_t)staticMeta.cta861.maxFrameAverageLightLevel, bytes, index);
1223 return true;
1224 #endif
1225 }
1226
PackVividMetadata(vector<uint8_t> & bytes,uint32_t & index,HdrMetadata & metadata)1227 static bool PackVividMetadata(vector<uint8_t>& bytes, uint32_t& index, HdrMetadata& metadata)
1228 {
1229 uint32_t dynamicMetadataSize = metadata.dynamicMetadata.size();
1230 const uint32_t metadataSize =
1231 UINT16_BYTE_COUNT + VIVID_STATIC_METADATA_SIZE_IN_IMAGE + UINT16_BYTE_COUNT + dynamicMetadataSize;
1232 PackVividPreInfo(bytes, index, false, false);
1233 ImageUtils::Uint16ToBytes(metadataSize, bytes, index);
1234 if (metadata.staticMetadata.empty()) {
1235 IMAGE_LOGI("HDR-IMAGE vivid staticMeta size is 0");
1236 ImageUtils::Uint16ToBytes(EMPTY_META_SIZE, bytes, index);
1237 } else if (!PackVividStaticMetadata(bytes, index, metadata.staticMetadata)) {
1238 return false;
1239 }
1240 ImageUtils::Uint16ToBytes(dynamicMetadataSize, bytes, index);
1241 bool cond = (!metadata.dynamicMetadata.empty() && memcpy_s(bytes.data() + index, bytes.size() - index,
1242 metadata.dynamicMetadata.data(), metadata.dynamicMetadata.size()) != EOK);
1243 CHECK_ERROR_RETURN_RET(cond, false);
1244 index += metadata.dynamicMetadata.size();
1245 PackExtendMetadata(bytes, index, metadata.extendMeta);
1246 return true;
1247 }
1248
PackVividMetadataMarker(HdrMetadata & metadata)1249 std::vector<uint8_t> HdrJpegPackerHelper::PackVividMetadataMarker(HdrMetadata& metadata)
1250 {
1251 uint32_t dynamicMetadataSize = metadata.dynamicMetadata.size();
1252 // metadataSize + staticMetadataSize + staticMetadata + dynamicMetadataSize + dynamicMetadata
1253 const uint32_t metadataSize = UINT16_BYTE_COUNT + UINT16_BYTE_COUNT + VIVID_STATIC_METADATA_SIZE_IN_IMAGE +
1254 UINT16_BYTE_COUNT + dynamicMetadataSize;
1255 uint32_t extendInfoSize = GetExtendMetadataSize(false, metadata.extendMeta);
1256 uint32_t markerLength = UINT32_BYTE_COUNT + ITUT35_TAG_SIZE + VIVID_METADATA_PRE_INFO_SIZE +
1257 metadataSize;
1258 if (extendInfoSize != EMPTY_SIZE) {
1259 markerLength += (UINT16_BYTE_COUNT + extendInfoSize);
1260 }
1261 vector<uint8_t> bytes(markerLength);
1262 uint32_t index = 0;
1263 bytes[index++] = JPEG_MARKER_PREFIX;
1264 bytes[index++] = JPEG_MARKER_APP8;
1265 uint32_t storeLength = markerLength - JPEG_MARKER_TAG_SIZE;
1266 ImageUtils::Uint16ToBytes(storeLength, bytes, index);
1267 ImageUtils::ArrayToBytes(ITUT35_TAG, ITUT35_TAG_SIZE, bytes, index);
1268 bool cond = PackVividMetadata(bytes, index, metadata);
1269 CHECK_ERROR_RETURN_RET_LOG(!cond, {}, "hdr image package metadata failed");
1270 return bytes;
1271 }
1272
PackISOExtendInfo(vector<uint8_t> & bytes,uint32_t & offset,ISOMetadata & metadata)1273 static void PackISOExtendInfo(vector<uint8_t>& bytes, uint32_t& offset, ISOMetadata& metadata)
1274 {
1275 ImageUtils::Int32ToBytes(
1276 (int32_t)(metadata.enhanceClippedThreholdMinGainmap[INDEX_ZERO] * DENOMINATOR), bytes, offset);
1277 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1278 ImageUtils::Int32ToBytes(
1279 (int32_t)(metadata.enhanceClippedThreholdMaxGainmap[INDEX_ZERO] * DENOMINATOR), bytes, offset);
1280 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1281 ImageUtils::Uint32ToBytes((uint32_t)(DENOMINATOR * metadata.enhanceMappingGamma[INDEX_ZERO]), bytes, offset);
1282 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1283 ImageUtils::Int32ToBytes(
1284 (int32_t)(metadata.enhanceMappingBaselineOffset[INDEX_ZERO] * DENOMINATOR), bytes, offset);
1285 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1286 ImageUtils::Int32ToBytes(
1287 (int32_t)(metadata.enhanceMappingAlternateOffset[INDEX_ZERO] * DENOMINATOR), bytes, offset);
1288 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1289
1290 ImageUtils::Int32ToBytes(
1291 (int32_t)(metadata.enhanceClippedThreholdMinGainmap[INDEX_ONE] * DENOMINATOR), bytes, offset);
1292 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1293 ImageUtils::Int32ToBytes(
1294 (int32_t)(metadata.enhanceClippedThreholdMaxGainmap[INDEX_ONE] * DENOMINATOR), bytes, offset);
1295 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1296 ImageUtils::Uint32ToBytes((uint32_t)(DENOMINATOR * metadata.enhanceMappingGamma[INDEX_ONE]), bytes, offset);
1297 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1298 ImageUtils::Int32ToBytes(
1299 (int32_t)(metadata.enhanceMappingBaselineOffset[INDEX_ONE] * DENOMINATOR), bytes, offset);
1300 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1301 ImageUtils::Int32ToBytes(
1302 (int32_t)(metadata.enhanceMappingAlternateOffset[INDEX_ONE] * DENOMINATOR), bytes, offset);
1303 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1304
1305 ImageUtils::Int32ToBytes(
1306 (int32_t)(metadata.enhanceClippedThreholdMinGainmap[INDEX_TWO] * DENOMINATOR), bytes, offset);
1307 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1308 ImageUtils::Int32ToBytes(
1309 (int32_t)(metadata.enhanceClippedThreholdMaxGainmap[INDEX_TWO] * DENOMINATOR), bytes, offset);
1310 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1311 ImageUtils::Uint32ToBytes((uint32_t)(DENOMINATOR * metadata.enhanceMappingGamma[INDEX_TWO]), bytes, offset);
1312 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1313 ImageUtils::Int32ToBytes(
1314 (int32_t)(metadata.enhanceMappingBaselineOffset[INDEX_TWO] * DENOMINATOR), bytes, offset);
1315 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1316 ImageUtils::Int32ToBytes(
1317 (int32_t)(metadata.enhanceMappingAlternateOffset[INDEX_TWO] * DENOMINATOR), bytes, offset);
1318 ImageUtils::Uint32ToBytes(DENOMINATOR, bytes, offset);
1319 }
1320
PackISOMetadataMarker(HdrMetadata & metadata)1321 vector<uint8_t> HdrJpegPackerHelper::PackISOMetadataMarker(HdrMetadata& metadata)
1322 {
1323 HDRVividExtendMetadata extendMeta = metadata.extendMeta;
1324 // flag(u8)+baseheadroomNumerator(u32)+baseheadroomDenomintor(u32)+altheadroom(u32)+altheadroomDenominator(u32)
1325 uint32_t isoInfoSize =
1326 UINT8_BYTE_COUNT + UINT32_BYTE_COUNT + UINT32_BYTE_COUNT + UINT32_BYTE_COUNT + UINT32_BYTE_COUNT;
1327 const uint32_t extendInfoMainParaNum = 10;
1328 const uint32_t isoExtendInfoSize = UINT32_BYTE_COUNT * extendInfoMainParaNum * THREE_COMPONENTS;
1329 // marker + isoGainmapTag + version + isoinfo + extendInfoMain
1330 uint32_t markerLength = UINT32_BYTE_COUNT + ISO_GAINMAP_TAG_SIZE + UINT32_BYTE_COUNT +
1331 isoInfoSize + isoExtendInfoSize;
1332 vector<uint8_t> bytes(markerLength);
1333 uint32_t index = 0;
1334 bytes[index++] = JPEG_MARKER_PREFIX;
1335 bytes[index++] = JPEG_MARKER_APP2;
1336 uint32_t storeLength = markerLength - JPEG_MARKER_TAG_SIZE;
1337 ImageUtils::Uint16ToBytes(storeLength, bytes, index);
1338 if (memcpy_s(bytes.data() + index, bytes.size() - index, ISO_GAINMAP_TAG, ISO_GAINMAP_TAG_SIZE) != EOK) {
1339 return {};
1340 }
1341 index += ISO_GAINMAP_TAG_SIZE;
1342 ImageUtils::Uint32ToBytes(extendMeta.metaISO.writeVersion, bytes, index);
1343 bytes[index] = 0x00;
1344 if (extendMeta.metaISO.useBaseColorFlag) {
1345 bytes[index] |= 0x40;
1346 }
1347 if (extendMeta.metaISO.gainmapChannelNum == THREE_GAINMAP_CHANNEL) {
1348 bytes[index] |= 0x80;
1349 }
1350 index++;
1351 uint32_t baseHeadroomNumerator = EMPTY_SIZE;
1352 if (extendMeta.metaISO.baseHeadroom > (float)EMPTY_SIZE) {
1353 baseHeadroomNumerator = (uint32_t)(extendMeta.metaISO.baseHeadroom * DENOMINATOR);
1354 }
1355 uint32_t baseHeadroomDenominator = DENOMINATOR;
1356 ImageUtils::Uint32ToBytes(baseHeadroomNumerator, bytes, index);
1357 ImageUtils::Uint32ToBytes(baseHeadroomDenominator, bytes, index);
1358 uint32_t altHeadroomNumerator = EMPTY_SIZE;
1359 uint32_t altHeadroomDenominator = 0x01;
1360 if (extendMeta.metaISO.alternateHeadroom > (float)EMPTY_SIZE) {
1361 altHeadroomNumerator = (uint32_t)(extendMeta.metaISO.alternateHeadroom * DENOMINATOR);
1362 altHeadroomDenominator = DENOMINATOR;
1363 }
1364 ImageUtils::Uint32ToBytes(altHeadroomNumerator, bytes, index);
1365 ImageUtils::Uint32ToBytes(altHeadroomDenominator, bytes, index);
1366 PackISOExtendInfo(bytes, index, extendMeta.metaISO);
1367 return bytes;
1368 }
1369
WriteJpegPreApp(sk_sp<SkData> & imageData,SkWStream & outputStream,uint32_t & index,uint32_t & jfifSize)1370 static bool WriteJpegPreApp(sk_sp<SkData>& imageData, SkWStream& outputStream, uint32_t& index, uint32_t& jfifSize)
1371 {
1372 const uint8_t* imageBytes = reinterpret_cast<const uint8_t*>(imageData->data());
1373 bool cond = *imageBytes != JPEG_MARKER_PREFIX || *(imageBytes + INDEX_ONE) != JPEG_SOI;
1374 CHECK_ERROR_RETURN_RET_LOG(cond, false, "hdr encode, the spliced image is not a jpeg");
1375 uint32_t dataSize = imageData->size();
1376 outputStream.write(imageBytes, JPEG_MARKER_TAG_SIZE);
1377 index += JPEG_MARKER_TAG_SIZE;
1378 while (index < dataSize) {
1379 cond = imageBytes[index] != JPEG_MARKER_PREFIX;
1380 CHECK_ERROR_RETURN_RET(cond, false);
1381 if ((imageBytes[index + INDEX_ONE] & 0xF0) != JPEG_MARKER_APP0) {
1382 return true;
1383 }
1384 uint16_t markerSize = (imageBytes[index + INDEX_TWO] << MOVE_ONE_BYTE) | imageBytes[index + INDEX_THREE];
1385 cond = markerSize > dataSize;
1386 CHECK_ERROR_RETURN_RET(cond, false);
1387 outputStream.write(imageBytes + index, markerSize + JPEG_MARKER_TAG_SIZE);
1388 if (imageBytes[index + INDEX_ONE] == JPEG_MARKER_APP0) {
1389 jfifSize = markerSize + JPEG_MARKER_TAG_SIZE;
1390 }
1391 index += (markerSize + JPEG_MARKER_TAG_SIZE);
1392 }
1393 return false;
1394 }
1395
SpliceLogHdrStream(sk_sp<SkData> & baseImage,SkWStream & output,Media::HdrMetadata & metadata)1396 uint32_t HdrJpegPackerHelper::SpliceLogHdrStream(sk_sp<SkData>& baseImage,
1397 SkWStream& output, Media::HdrMetadata& metadata)
1398 {
1399 if (baseImage == nullptr) {
1400 return ERR_IMAGE_ENCODE_FAILED;
1401 }
1402 uint32_t offset = 0;
1403 uint32_t jfifSize = 0;
1404 if (!WriteJpegPreApp(baseImage, output, offset, jfifSize)) {
1405 return ERR_IMAGE_ENCODE_FAILED;
1406 }
1407 std::vector<uint8_t> HdrMediaTypeInfo = PackHdrMediaTypeMarker(metadata);
1408 output.write(HdrMediaTypeInfo.data(), HdrMediaTypeInfo.size());
1409 const uint8_t* baseBytes = reinterpret_cast<const uint8_t*>(baseImage->data());
1410 output.write(baseBytes + offset, baseImage->size() - offset);
1411 return SUCCESS;
1412 }
1413
SpliceHdrStream(sk_sp<SkData> & baseImage,sk_sp<SkData> & gainmapImage,SkWStream & output,HdrMetadata & metadata)1414 uint32_t HdrJpegPackerHelper::SpliceHdrStream(sk_sp<SkData>& baseImage, sk_sp<SkData>& gainmapImage,
1415 SkWStream& output, HdrMetadata& metadata)
1416 {
1417 bool cond = (baseImage == nullptr || gainmapImage == nullptr);
1418 CHECK_ERROR_RETURN_RET(cond, ERR_IMAGE_ENCODE_FAILED);
1419 uint32_t offset = 0;
1420 uint32_t jfifSize = 0;
1421 cond = WriteJpegPreApp(baseImage, output, offset, jfifSize);
1422 CHECK_ERROR_RETURN_RET(!cond, ERR_IMAGE_ENCODE_FAILED);
1423 IMAGE_LOGI("HDR-IMAGE spliceHdr metadata size ,static %{public}zu, dynamic %{public}zu",
1424 metadata.staticMetadata.size(), metadata.dynamicMetadata.size());
1425 std::vector<uint8_t> gainmapMetadataPack = PackVividMetadataMarker(metadata);
1426 std::vector<uint8_t> gainmapISOMetadataPack = PackISOMetadataMarker(metadata);
1427 std::vector<uint8_t> gainmapHdrMediaTypeInfo = PackHdrMediaTypeMarker(metadata);
1428 uint32_t gainmapSize = gainmapImage->size() + gainmapMetadataPack.size() +
1429 gainmapISOMetadataPack.size() + gainmapHdrMediaTypeInfo.size();
1430 std::vector<uint8_t> baseISOInfo = PackBaseISOMarker();
1431 uint32_t baseVividApp8Size = GetBaseVividMarkerSize();
1432 uint32_t baseMpfApp2Size = GetMpfMarkerSize();
1433 uint32_t baseSize = baseImage->size() + baseISOInfo.size() + baseVividApp8Size + baseMpfApp2Size;
1434 uint32_t allAppSize = offset + baseISOInfo.size() + baseVividApp8Size + baseMpfApp2Size;
1435 std::vector<uint8_t> baseVividInfo = PackBaseVividMarker(baseSize, offset, allAppSize);
1436 std::vector<uint8_t> mpfInfo = PackBaseMpfMarker(baseSize, gainmapSize, offset + baseVividApp8Size);
1437 output.write(baseVividInfo.data(), baseVividInfo.size());
1438 output.write(mpfInfo.data(), mpfInfo.size());
1439 output.write(baseISOInfo.data(), baseISOInfo.size());
1440 const uint8_t* baseBytes = reinterpret_cast<const uint8_t*>(baseImage->data());
1441 output.write(baseBytes + offset, baseImage->size() - offset);
1442
1443 // write gainmap
1444 const uint8_t* gainmapBytes = reinterpret_cast<const uint8_t*>(gainmapImage->data());
1445 output.write(gainmapBytes, JPEG_MARKER_TAG_SIZE);
1446 output.write(gainmapISOMetadataPack.data(), gainmapISOMetadataPack.size());
1447 output.write(gainmapMetadataPack.data(), gainmapMetadataPack.size());
1448 output.write(gainmapHdrMediaTypeInfo.data(), gainmapHdrMediaTypeInfo.size());
1449 output.write(gainmapBytes + JPEG_MARKER_TAG_SIZE, gainmapImage->size() - JPEG_MARKER_TAG_SIZE);
1450 return SUCCESS;
1451 }
1452
PackIT35Info(HdrMetadata & metadata,std::vector<uint8_t> & info)1453 bool HdrHeifPackerHelper::PackIT35Info(HdrMetadata& metadata, std::vector<uint8_t>& info)
1454 {
1455 uint32_t dynamicMetadataSize = metadata.dynamicMetadata.size();
1456
1457 // metadataSize + staticMetadataSize + staticMetadata + dynamicMetadataSize + dynamicMetadata
1458 const uint32_t metadataSize = UINT16_BYTE_COUNT + UINT16_BYTE_COUNT + VIVID_STATIC_METADATA_SIZE_IN_IMAGE +
1459 UINT16_BYTE_COUNT + dynamicMetadataSize;
1460 uint32_t extendInfoSize = GetExtendMetadataSize(false, metadata.extendMeta);
1461 uint32_t infoLength = VIVID_METADATA_PRE_INFO_SIZE + metadataSize;
1462 if (extendInfoSize != EMPTY_SIZE) {
1463 infoLength += (UINT16_BYTE_COUNT + extendInfoSize);
1464 }
1465 info.resize(infoLength);
1466 uint32_t index = 0;
1467 bool cond = !PackVividMetadata(info, index, metadata);
1468 CHECK_ERROR_RETURN_RET_LOG(cond, false, "hdr image package metadata failed");
1469 return true;
1470 }
1471 }
1472 }