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
18namespace OHOS::VDI::HEIF {
19using namespace OHOS::HDI::Codec::Image::V2_0;
20using namespace std;
21
22void 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
53bool 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
72void HeifEncoderHelper::Reset()
73{
74    inputImgs_.clear();
75    inputMetas_.clear();
76    refs_.clear();
77}
78
79bool 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
130bool 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
159bool 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
200bool 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
238bool 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
265bool 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
288bool 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
299bool 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
320bool 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