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 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 "codec_heif_helper.h"
17 
18 namespace OHOS::VDI::HEIF {
19 using namespace OHOS::HDI::Codec::Image::V2_0;
20 using namespace std;
21 
DoEncode()22 void HeifEncoderHelper::DoEncode()
23 {
24     HDF_LOGI("start heif encode");
25     Reset();
26     bool flag = false;
27     if (encodeOpt_.gainMapPath.length() > 0) {
28         HDF_LOGI("AssembleParamForTmap");
29         flag = AssembleParamForTmap();
30     } else {
31         HDF_LOGI("AssembleParamForPrimaryImg");
32         flag = AssembleParamForPrimaryImg();
33     }
34     IF_TRUE_RETURN(!flag);
35     HDF_LOGI("get ICodecImage");
36     sptr<ICodecImage> hdiHeifEncoder = ICodecImage::Get();
37     IF_TRUE_RETURN_WITH_MSG(hdiHeifEncoder == nullptr, "failed to get ICodecImage");
38     SharedBuffer output;
39     IF_TRUE_RETURN(!AllocOutputBuffer(output));
40     uint32_t filledLen = 0;
41     HDF_LOGI("DoHeifEncode");
42     int32_t ret = hdiHeifEncoder->DoHeifEncode(inputImgs_, inputMetas_, refs_, output, filledLen);
43     if (ret == HDF_SUCCESS) {
44         HDF_LOGI("heif encode succeed");
45         output.filledLen = filledLen;
46         bufferHelper_.DumpBuffer(encodeOpt_.outputPath, output);
47     } else {
48         HDF_LOGE("heif encode failed");
49     }
50     close(output.fd);
51 }
52 
AllocOutputBuffer(SharedBuffer & output)53 bool HeifEncoderHelper::AllocOutputBuffer(SharedBuffer& output)
54 {
55     static constexpr size_t EXTERNAL_BUFFER_SIZE = 18 * 1024 * 1024;
56     int fd = AshmemCreate("ForHeifEditOut", EXTERNAL_BUFFER_SIZE);
57     bool flag = true;
58     if (fd >= 0) {
59         output.fd = fd;
60         output.capacity = static_cast<uint32_t>(AshmemGetSize(fd));
61     } else {
62         flag = false;
63         output.fd = -1;
64         output.capacity = 0;
65         HDF_LOGE("failed to create output buffer");
66     }
67     output.filledLen = 0;
68     return flag;
69 }
70 
71 
Reset()72 void HeifEncoderHelper::Reset()
73 {
74     inputImgs_.clear();
75     inputMetas_.clear();
76     refs_.clear();
77 }
78 
AddPropOnlyForTmap(ByteWriter & bw)79 bool HeifEncoderHelper::AddPropOnlyForTmap(ByteWriter& bw)
80 {
81     MasteringDisplayColourVolume clrVol = {
82         .displayPrimariesRX = 1,
83         .displayPrimariesRY = 2,
84         .displayPrimariesGX = 3,
85         .displayPrimariesGY = 4,
86         .displayPrimariesBX = 5,
87         .displayPrimariesBY = 6,
88         .whitePointX = 0,
89         .whitePointY = 0,
90         .maxDisplayMasteringLuminance = 0,
91         .minDisplayMasteringLuminance = 0
92     };
93     IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<MasteringDisplayColourVolume>(MASTER_DISPLAY_COLOR_VOLUME, clrVol), false,
94                                 "failed to add MASTER_DISPLAY_COLOR_VOLUME");
95     HDF_LOGI("add MASTER_DISPLAY_COLOR_VOLUME succeed");
96 
97     ToneMapMetadata tmapMeta;
98     static constexpr uint8_t MULTI_CHANNEL = 3;
99     tmapMeta.channelCnt = MULTI_CHANNEL;
100     tmapMeta.useBaseColorSpace = true;
101     tmapMeta.baseHdrHeadroom = {12, 23};
102     tmapMeta.alternateHdrHeadroom = {36, 62};
103     tmapMeta.channels1 = {
104         .gainMapMin = {5, 21},
105         .gainMapMax = {5, 7},
106         .gamma = {2, 7},
107         .baseOffset = {1, 3},
108         .alternateOffset = {1, 7}
109     };
110     tmapMeta.channels2 = {
111         .gainMapMin = {5, 21},
112         .gainMapMax = {5, 7},
113         .gamma = {2, 7},
114         .baseOffset = {1, 3},
115         .alternateOffset = {1, 7}
116     };
117     tmapMeta.channels3 = {
118         .gainMapMin = {5, 21},
119         .gainMapMax = {5, 7},
120         .gamma = {2, 7},
121         .baseOffset = {1, 3},
122         .alternateOffset = {1, 7}
123     };
124     IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<ToneMapMetadata>(TONE_MAP_METADATA, tmapMeta), false,
125                                 "failed to add TONE_MAP_METADATA");
126     HDF_LOGI("add TONE_MAP_METADATA succeed");
127     return true;
128 }
129 
AddPropMirrorAndRotate(ByteWriter & bw)130 bool HeifEncoderHelper::AddPropMirrorAndRotate(ByteWriter& bw)
131 {
132     static map<ImageMirror, bool> mirrorMap = {
133         { ImageMirror::HORIZONTAL, false },
134         { ImageMirror::VERTICAL,   true },
135     };
136     auto iterMirror = mirrorMap.find(encodeOpt_.mirrorInfo);
137     if (iterMirror != mirrorMap.end()) {
138         bool isMirrorVertical = iterMirror->second;
139         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<bool>(MIRROR_INFO, isMirrorVertical), false,
140                                     "failed to add MIRROR_INFO");
141         HDF_LOGI("add MIRROR_INFO succeed");
142     }
143 
144     static map<ImageRotation, uint32_t> rotateMap = {
145         { ImageRotation::ANTI_CLOCKWISE_90,  90 },
146         { ImageRotation::ANTI_CLOCKWISE_180, 180 },
147         { ImageRotation::ANTI_CLOCKWISE_270, 270 },
148     };
149     auto iterRotate = rotateMap.find(encodeOpt_.rotateInfo);
150     if (iterRotate != rotateMap.end()) {
151         uint32_t rotateDegree = iterRotate->second;
152         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<uint32_t>(ROTATE_INFO, rotateDegree), false,
153                                     "failed to add ROTATE_INFO");
154         HDF_LOGI("add ROTATE_INFO succeed");
155     }
156     return true;
157 }
158 
CreateImgParam(ImgType type,vector<uint8_t> & props)159 bool HeifEncoderHelper::CreateImgParam(ImgType type, vector<uint8_t>& props)
160 {
161     ByteWriter bw;
162 
163     if (type != T_MAP) {
164         IF_TRUE_RETURN_VAL(!AddPropMirrorAndRotate(bw), false);
165     }
166 
167     ColorType clrType = encodeOpt_.iccProfilePath.length() > 0 ? PROF : NCLX;
168     IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<ColorType>(COLOR_TYPE, clrType), false, "failed to add COLOR_TYPE");
169     HDF_LOGI("add COLOR_TYPE succeed");
170 
171     if (clrType == NCLX) {
172         ColourInfo clrInfo = {
173             .colourPrimaries = 2,
174             .transferCharacteristics = 2,
175             .matrixCoefficients = 2,
176             .fullRangeFlag = false
177         };
178         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<ColourInfo>(COLOR_INFO, clrInfo), false, "failed to add COLOR_INFO");
179         HDF_LOGI("add COLOR_INFO succeed");
180     }
181 
182     if (type == T_MAP || type == PRIMARY_IMG) {
183         ContentLightLevel level = {
184             .maxContentLightLevel = 1,
185             .maxPicAverageLightLevel = 2
186         };
187         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<ContentLightLevel>(CONTENT_LIGHT_LEVEL, level), false,
188                                     "failed to add CONTENT_LIGHT_LEVEL");
189         HDF_LOGI("add CONTENT_LIGHT_LEVEL succeed");
190     }
191 
192     if (type == T_MAP) {
193         IF_TRUE_RETURN_VAL(!AddPropOnlyForTmap(bw), false);
194     }
195 
196     IF_TRUE_RETURN_VAL_WITH_MSG(!bw.Finalize(props), false, "failed to write img prop");
197     return true;
198 }
199 
FillImageItem(ImgType type,ImageItem & item)200 bool HeifEncoderHelper::FillImageItem(ImgType type, ImageItem& item)
201 {
202     map<ImgType, string> typeToFile = {
203         { PRIMARY_IMG,   encodeOpt_.primaryImgPath },
204         { AUXILIARY_IMG, encodeOpt_.auxiliaryImgPath },
205         { THUMBNAIL_IMG, encodeOpt_.thumbnailImgPath },
206         { GAIN_MAP,      encodeOpt_.gainMapPath },
207         { T_MAP,         "" },
208     };
209     item.itemName = "";
210     item.id = GetNextId();
211     item.sharedProperties = {
212         .fd = -1,
213         .filledLen = 0,
214         .capacity = 0
215     };
216     item.pixelBuffer = bufferHelper_.CreateImgBuffer(typeToFile[type]);
217     IF_TRUE_RETURN_VAL((type != T_MAP && item.pixelBuffer == nullptr), false);
218     item.isPrimary = (type == PRIMARY_IMG);
219     item.isHidden = (type != PRIMARY_IMG);
220     item.compressType = (type == T_MAP ? "none" : "hevc");
221     static constexpr uint32_t ENCODE_QUALITY = 85;
222     item.quality = ENCODE_QUALITY;
223     IF_TRUE_RETURN_VAL(!CreateImgParam(type, item.liteProperties), false);
224     map<PropertyType, string> sharedProps;
225     if (encodeOpt_.iccProfilePath.length() > 0) {
226         HDF_LOGI("add ICC_PROFILE");
227         sharedProps[ICC_PROFILE] = encodeOpt_.iccProfilePath;
228     }
229     if (type == T_MAP && encodeOpt_.it35Path.length() > 0) {
230         HDF_LOGI("add IT35_INFO");
231         sharedProps[IT35_INFO] = encodeOpt_.it35Path;
232     }
233     IF_TRUE_RETURN_VAL(sharedProps.empty(), true);
234     item.sharedProperties = bufferHelper_.CreateSharedBuffer(sharedProps);
235     return (item.sharedProperties.fd >= 0);
236 }
237 
AssembleParamForOtherImg(uint32_t primaryImgId)238 bool HeifEncoderHelper::AssembleParamForOtherImg(uint32_t primaryImgId)
239 {
240     if (encodeOpt_.auxiliaryImgPath.length() > 0) {
241         ImageItem itemAuxlImg;
242         IF_TRUE_RETURN_VAL(!FillImageItem(AUXILIARY_IMG, itemAuxlImg), false);
243         inputImgs_.emplace_back(itemAuxlImg);
244         refs_.emplace_back(ItemRef {
245             .type = AUXL,
246             .auxType = "",
247             .from = itemAuxlImg.id,
248             .to = {primaryImgId}
249         });
250     }
251     if (encodeOpt_.thumbnailImgPath.length() > 0) {
252         ImageItem itemThmbImg;
253         IF_TRUE_RETURN_VAL(!FillImageItem(THUMBNAIL_IMG, itemThmbImg), false);
254         inputImgs_.emplace_back(itemThmbImg);
255         refs_.emplace_back(ItemRef {
256             .type = THMB,
257             .auxType = "",
258             .from = itemThmbImg.id,
259             .to = {primaryImgId}
260         });
261     }
262     return true;
263 }
264 
AssembleParamForTmap()265 bool HeifEncoderHelper::AssembleParamForTmap()
266 {
267     ImageItem itemTmap;
268     ImageItem itemPrimaryImg;
269     ImageItem itemGainMap;
270     IF_TRUE_RETURN_VAL(!FillImageItem(T_MAP, itemTmap), false);
271     IF_TRUE_RETURN_VAL(!FillImageItem(PRIMARY_IMG, itemPrimaryImg), false);
272     IF_TRUE_RETURN_VAL(!FillImageItem(GAIN_MAP, itemGainMap), false);
273     inputImgs_.emplace_back(itemTmap);
274     inputImgs_.emplace_back(itemPrimaryImg);
275     inputImgs_.emplace_back(itemGainMap);
276     refs_.emplace_back(ItemRef {
277         .type = DIMG,
278         .auxType = "",
279         .from = itemTmap.id,
280         .to = {itemPrimaryImg.id, itemGainMap.id}
281     });
282     if (AssembleParamForOtherImg(itemPrimaryImg.id)) {
283         return AssembleParamForMetaData(itemPrimaryImg.id);
284     }
285     return false;
286 }
287 
AssembleParamForPrimaryImg()288 bool HeifEncoderHelper::AssembleParamForPrimaryImg()
289 {
290     ImageItem itemPrimaryImg;
291     IF_TRUE_RETURN_VAL(!FillImageItem(PRIMARY_IMG, itemPrimaryImg), false);
292     inputImgs_.emplace_back(itemPrimaryImg);
293     if (AssembleParamForOtherImg(itemPrimaryImg.id)) {
294         return AssembleParamForMetaData(itemPrimaryImg.id);
295     }
296     return false;
297 }
298 
FillMetaItem(const string & metaFile,MetaType type,MetaItem & item)299 bool HeifEncoderHelper::FillMetaItem(const string& metaFile, MetaType type, MetaItem& item)
300 {
301     item.itemName = "";
302     item.id = GetNextId();
303     item.properties = {};
304     if (type == USER_DATA) {
305         static constexpr char USER_DATA_LABEL[] = "userdata";
306         item.itemName = USER_DATA_LABEL;
307         bool useCompress = true;
308         ByteWriter bw;
309         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.AddData<bool>(USER_DATA_DO_COMPRESS, useCompress), false,
310                                     "failed to add USER_DATA_DO_COMPRESS");
311         IF_TRUE_RETURN_VAL_WITH_MSG(!bw.Finalize(item.properties), false, "failed to write USER_DATA_DO_COMPRESS");
312     } else if (type == EXIF_DATA) {
313         static constexpr char EXIF_LABEL[] = "exif";
314         item.itemName = EXIF_LABEL;
315     }
316     item.data = bufferHelper_.CreateSharedBuffer(metaFile);
317     return (item.data.fd >= 0);
318 }
319 
AssembleParamForMetaData(uint32_t primaryImgId)320 bool HeifEncoderHelper::AssembleParamForMetaData(uint32_t primaryImgId)
321 {
322     HDF_LOGI("AssembleParamForMetaData");
323     if (encodeOpt_.exifDataPath.length() > 0) {
324         HDF_LOGI("add exif: %{public}s", encodeOpt_.exifDataPath.c_str());
325         MetaItem metaExifData;
326         IF_TRUE_RETURN_VAL(!FillMetaItem(encodeOpt_.exifDataPath, EXIF_DATA, metaExifData), false);
327         inputMetas_.emplace_back(metaExifData);
328         refs_.emplace_back(ItemRef {
329             .type = CDSC,
330             .auxType = "",
331             .from = metaExifData.id,
332             .to = {primaryImgId}
333         });
334     }
335     if (encodeOpt_.userDataPath.length() > 0) {
336         HDF_LOGI("add userData: %{public}s", encodeOpt_.userDataPath.c_str());
337         MetaItem metaUserData;
338         IF_TRUE_RETURN_VAL(!FillMetaItem(encodeOpt_.userDataPath, USER_DATA, metaUserData), false);
339         inputMetas_.emplace_back(metaUserData);
340         refs_.emplace_back(ItemRef {
341             .type = CDSC,
342             .auxType = "",
343             .from = metaUserData.id,
344             .to = {primaryImgId}
345         });
346     }
347     return true;
348 }
349 }
350