• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <charconv>
17 #include <dlfcn.h>
18 #ifdef USE_M133_SKIA
19 #include <unistd.h>
20 #endif
21 
22 #include "astc_codec.h"
23 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
24 #include "image_compressor.h"
25 #endif
26 #include "image_log.h"
27 #include "image_trace.h"
28 #include "image_system_properties.h"
29 #include "image_trace.h"
30 #include "securec.h"
31 #include "media_errors.h"
32 #if !defined(_WIN32) && !defined(_APPLE) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
33 #include "v1_0/buffer_handle_meta_key_type.h"
34 #include "vpe_utils.h"
35 #endif
36 
37 #undef LOG_DOMAIN
38 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
39 
40 #undef LOG_TAG
41 #define LOG_TAG "AstcCodec"
42 
43 namespace OHOS {
44 namespace ImagePlugin {
45 using namespace Media;
46 #if !defined(_WIN32) && !defined(_APPLE) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
47 using namespace OHOS::HDI::Display::Graphic::Common::V1_0;
48 #endif
49 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
50 using namespace AstcEncBasedCl;
51 #endif
52 constexpr uint8_t TEXTURE_HEAD_BYTES = 16;
53 constexpr uint8_t ASTC_MASK = 0xFF;
54 constexpr uint8_t ASTC_NUM_4 = 4;   // represents ASTC_EXTEND_INFO_SIZE_DEFINITION_LENGTH
55 constexpr uint8_t ASTC_NUM_8 = 8;
56 constexpr uint8_t ASTC_HEADER_SIZE = 16;
57 constexpr uint8_t ASTC_NUM_24 = 24;
58 static const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
59 constexpr uint8_t DEFAULT_DIM = 4;
60 constexpr uint8_t MAX_DIM = 12;
61 constexpr uint8_t HIGH_SPEED_PROFILE_MAP_QUALITY = 20; // quality level is 20 for thumbnail
62 constexpr uint8_t RGBA_BYTES_PIXEL_LOG2 = 2;
63 constexpr uint8_t MASKBITS_FOR_8BITS = 255;
64 constexpr uint8_t UINT32_1TH_BYTES = 8;
65 constexpr uint8_t UINT32_2TH_BYTES = 16;
66 constexpr uint8_t UINT32_3TH_BYTES = 24;
67 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
68 constexpr int32_t WIDTH_CL_THRESHOLD = 256;
69 constexpr int32_t HEIGHT_CL_THRESHOLD = 256;
70 #endif
71 constexpr int32_t WIDTH_MAX_ASTC = 8192;
72 constexpr int32_t HEIGHT_MAX_ASTC = 8192;
73 
74 #ifdef SUT_ENCODE_ENABLE
CheckClBinIsExist(const std::string & name)75 static bool CheckClBinIsExist(const std::string &name)
76 {
77     return (access(name.c_str(), F_OK) != -1); // -1 means that the file is  not exist
78 }
79 #endif
80 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
CheckClBinIsExistWithLock(const std::string & name)81 static bool CheckClBinIsExistWithLock(const std::string &name)
82 {
83     std::lock_guard<std::mutex> lock(checkClBinPathMutex);
84     return (access(name.c_str(), F_OK) != -1); // -1 means that the file is  not exist
85 }
86 #endif
87 
88 struct AstcEncCheckInfo {
89     uint32_t pixmapInSize = 0;
90     uint32_t astcBufferSize = 0;
91     uint32_t extendInfoSize = 0;
92     uint32_t extendBufferSize = 0;
93     PixelFormat pixmapFormat = PixelFormat::UNKNOWN;
94 };
95 
96 #ifdef SUT_ENCODE_ENABLE
97 constexpr uint8_t EXPAND_ASTC_INFO_MAX_ENC = 16;
98 constexpr int32_t EXPAND_SIZE_BYTES_ENC = 4;
99 
100 struct AstcInInfo {
101     const uint8_t* astcBuf;
102     int32_t astcBytes;
103     uint8_t expandNums;
104     uint8_t expandInfoType[EXPAND_ASTC_INFO_MAX_ENC];
105     int32_t expandInfoBytes[EXPAND_ASTC_INFO_MAX_ENC];
106     uint8_t* expandInfoBuf[EXPAND_ASTC_INFO_MAX_ENC];
107 };
108 struct SutOutInfo {
109     uint8_t* sutBuf;
110     int32_t sutCapacity;
111     int32_t sutBytes;
112 };
113 #ifdef SUT_PATH_X64
114 static const std::string g_textureSuperEncSo = "/system/lib64/module/hms/graphic/libtextureSuperCompress.z.so";
115 #else
116 static const std::string g_textureSuperEncSo = "/system/lib/module/hms/graphic/libtextureSuperCompress.z.so";
117 #endif
118 using SuperCompressTexture = bool (*)(const AstcInInfo&, SutOutInfo&, uint32_t);
119 class SutEncSoManager {
120 public:
121     SutEncSoManager();
122     ~SutEncSoManager();
123     bool LoadSutEncSo();
124     SuperCompressTexture sutEncSoEncFunc_;
125 private:
126     bool sutEncSoOpened_;
127     void *textureEncSoHandle_;
128     std::mutex sutEncSoMutex_ = {};
129 };
130 
131 static SutEncSoManager g_sutEncSoManager;
132 
SutEncSoManager()133 SutEncSoManager::SutEncSoManager()
134 {
135     sutEncSoOpened_ = false;
136     textureEncSoHandle_ = nullptr;
137     sutEncSoEncFunc_ = nullptr;
138 }
139 
~SutEncSoManager()140 SutEncSoManager::~SutEncSoManager()
141 {
142     bool sutEncHasBeenOpen = sutEncSoOpened_ && (textureEncSoHandle_ != nullptr);
143     if (sutEncHasBeenOpen) {
144         int ret = dlclose(textureEncSoHandle_);
145         IMAGE_LOGD("astcenc dlcose ret: %{public}d %{public}s!", ret, g_textureSuperEncSo.c_str());
146     }
147 }
148 
LoadSutEncSo()149 bool SutEncSoManager::LoadSutEncSo()
150 {
151     std::lock_guard<std::mutex> lock(sutEncSoMutex_);
152     if (!sutEncSoOpened_) {
153         if (!CheckClBinIsExist(g_textureSuperEncSo)) {
154             IMAGE_LOGE("sut %{public}s! is not found", g_textureSuperEncSo.c_str());
155             return false;
156         }
157         textureEncSoHandle_ = dlopen(g_textureSuperEncSo.c_str(), 1);
158         if (textureEncSoHandle_ == nullptr) {
159             IMAGE_LOGE("sut libtextureSuperCompress dlopen failed!");
160             return false;
161         }
162         sutEncSoEncFunc_ =
163             reinterpret_cast<SuperCompressTexture>(dlsym(textureEncSoHandle_, "SuperCompressTextureTlv"));
164         if (sutEncSoEncFunc_ == nullptr) {
165             IMAGE_LOGE("sut libtextureSuperCompress dlsym failed!");
166             dlclose(textureEncSoHandle_);
167             textureEncSoHandle_ = nullptr;
168             return false;
169         }
170         IMAGE_LOGD("astcenc dlopen success: %{public}s!", g_textureSuperEncSo.c_str());
171         sutEncSoOpened_ = true;
172     }
173     return true;
174 }
175 #endif
176 
SetAstcEncode(OutputDataStream * outputStream,PlEncodeOptions & option,Media::PixelMap * pixelMap)177 uint32_t AstcCodec::SetAstcEncode(OutputDataStream* outputStream, PlEncodeOptions &option, Media::PixelMap* pixelMap)
178 {
179     if (outputStream == nullptr || pixelMap == nullptr) {
180         IMAGE_LOGE("input data is nullptr.");
181         return ERROR;
182     }
183     astcOutput_ = outputStream;
184     astcOpts_ = option;
185     astcPixelMap_ = pixelMap;
186     return SUCCESS;
187 }
188 
189 // test ASTCEncoder
GenAstcHeader(uint8_t * header,astcenc_image img,TextureEncodeOptions & encodeParams)190 uint32_t GenAstcHeader(uint8_t *header, astcenc_image img, TextureEncodeOptions &encodeParams)
191 {
192     uint8_t *tmp = header;
193     *tmp++ = ASTC_MAGIC_ID & ASTC_MASK;
194     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_8) & ASTC_MASK;
195     *tmp++ = (ASTC_MAGIC_ID >> ASTC_HEADER_SIZE) & ASTC_MASK;
196     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_24) & ASTC_MASK;
197     *tmp++ = static_cast<uint8_t>(encodeParams.blockX_);
198     *tmp++ = static_cast<uint8_t>(encodeParams.blockY_);
199     *tmp++ = 1;
200     *tmp++ = img.dim_x & ASTC_MASK;
201     *tmp++ = (img.dim_x >> ASTC_NUM_8) & ASTC_MASK;
202     *tmp++ = (img.dim_x >> ASTC_HEADER_SIZE) & ASTC_MASK;
203     *tmp++ = img.dim_y & ASTC_MASK;
204     *tmp++ = (img.dim_y >> ASTC_NUM_8) & ASTC_MASK;
205     *tmp++ = (img.dim_y >> ASTC_HEADER_SIZE) & ASTC_MASK;
206     *tmp++ = img.dim_z & ASTC_MASK;
207     *tmp++ = (img.dim_z >> ASTC_NUM_8) & ASTC_MASK;
208     *tmp++ = (img.dim_z >> ASTC_HEADER_SIZE) & ASTC_MASK;
209     return SUCCESS;
210 }
211 
InitAstcencConfig(AstcEncoder * work,TextureEncodeOptions * option)212 uint32_t InitAstcencConfig(AstcEncoder* work, TextureEncodeOptions* option)
213 {
214     bool invaildInput = (work == nullptr) || (option == nullptr);
215     if (invaildInput) {
216         IMAGE_LOGE("astc input work or option is nullptr.");
217         return ERROR;
218     }
219     unsigned int blockX = option->blockX_;
220     unsigned int blockY = option->blockY_;
221     unsigned int blockZ = 1;
222 
223     float quality = ASTCENC_PRE_FAST;
224     unsigned int flags = ASTCENC_FLG_SELF_DECOMPRESS_ONLY;
225     astcenc_error status = astcenc_config_init(work->profile, blockX, blockY,
226         blockZ, quality, flags, &work->config);
227     if (status != ASTCENC_SUCCESS) {
228         IMAGE_LOGE("ERROR: astcenc_config_init failed, status %{public}d", status);
229         return ERROR;
230     }
231     work->config.privateProfile = option->privateProfile_;
232     if (work->config.privateProfile == HIGH_SPEED_PROFILE) {
233         work->config.tune_refinement_limit = 1;
234         work->config.tune_candidate_limit = 1;
235         work->config.tune_partition_count_limit = 1;
236     }
237     if (astcenc_context_alloc(&work->config, 1, &work->codec_context) != ASTCENC_SUCCESS) {
238         return ERROR;
239     }
240     return SUCCESS;
241 }
242 
extractDimensions(std::string & format,TextureEncodeOptions & param)243 void extractDimensions(std::string &format, TextureEncodeOptions &param)
244 {
245     param.blockX_ = DEFAULT_DIM;
246     param.blockY_ = DEFAULT_DIM;
247     std::size_t slashPos = format.rfind('/');
248     if (slashPos != std::string::npos) {
249         std::string dimensions = format.substr(slashPos + 1);
250         std::size_t starPos = dimensions.find('*');
251         if (starPos != std::string::npos) {
252             std::string widthStr = dimensions.substr(0, starPos);
253             std::string heightStr = dimensions.substr(starPos + 1);
254 
255             auto ret_x = std::from_chars(widthStr.data(), widthStr.data() + widthStr.size(), param.blockX_);
256             auto ret_y = std::from_chars(heightStr.data(), heightStr.data() + heightStr.size(), param.blockY_);
257             if (!(ret_x.ec == std::errc() && ret_y.ec == std::errc())) {
258                 IMAGE_LOGE("Failed to convert string to number");
259             }
260         }
261     }
262 }
263 
264 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
265 constexpr double MAX_PSNR = 99.9;
266 constexpr double MAX_VALUE = 255;
267 constexpr double THRESHOLD_R = 30.0;
268 constexpr double THRESHOLD_G = 30.0;
269 constexpr double THRESHOLD_B = 30.0;
270 constexpr double THRESHOLD_A = 30.0;
271 constexpr double THRESHOLD_RGB = 30.0;
272 constexpr double LOG_BASE = 10.0;
CheckQuality(int32_t * mseIn[RGBA_COM],int blockNum,int blockXYZ)273 bool CheckQuality(int32_t *mseIn[RGBA_COM], int blockNum, int blockXYZ)
274 {
275     double psnr[RGBA_COM + 1];
276     const double threshold[RGBA_COM + 1] = {THRESHOLD_R, THRESHOLD_G, THRESHOLD_B, THRESHOLD_A, THRESHOLD_RGB};
277     uint64_t mseTotal[RGBA_COM + 1] = {0, 0, 0, 0, 0};
278     for (int i = R_COM; i < RGBA_COM; i++) {
279         int32_t *mse = mseIn[i];
280         if (!mse) {
281             return false;
282         }
283         for (int j = 0; j < blockNum; j++) {
284             mseTotal[i] += *mse;
285             if (i != A_COM) mseTotal[RGBA_COM] += *mse;
286             mse++;
287         }
288     }
289     for (int i = R_COM; i < RGBA_COM; i++) {
290         if (mseTotal[i] == 0) {
291             psnr[i] = MAX_PSNR;
292             continue;
293         }
294         double mseRgb = static_cast<double>(mseTotal[i] / static_cast<uint64_t>((blockNum * blockXYZ)));
295         psnr[i] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
296     }
297     if (mseTotal[RGBA_COM] == 0) {
298         psnr[RGBA_COM] = MAX_PSNR;
299     } else {
300         double mseRgb = static_cast<double>(
301             mseTotal[RGBA_COM] / static_cast<uint64_t>((blockNum * blockXYZ * (RGBA_COM - 1))));
302         psnr[RGBA_COM] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
303     }
304     IMAGE_LOGD("astc psnr r%{public}f g%{public}f b%{public}f a%{public}f rgb%{public}f",
305         psnr[R_COM], psnr[G_COM], psnr[B_COM], psnr[A_COM],
306         psnr[RGBA_COM]);
307     return (psnr[R_COM] > threshold[R_COM]) && (psnr[G_COM] > threshold[G_COM])
308         && (psnr[B_COM] > threshold[B_COM]) && (psnr[A_COM] > threshold[A_COM])
309         && (psnr[RGBA_COM] > threshold[RGBA_COM]);
310 }
311 #endif
312 
FreeMem(AstcEncoder * work)313 static void FreeMem(AstcEncoder *work)
314 {
315     if (!work) {
316         return;
317     }
318 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
319     if (work->calQualityEnable) {
320         for (int i = R_COM; i < RGBA_COM; i++) {
321             if (work->mse[i]) {
322                 free(work->mse[i]);
323                 work->mse[i] = nullptr;
324             }
325         }
326     }
327 #endif
328     if (work->image_.data) {
329         free(work->image_.data);
330         work->image_.data = nullptr;
331     }
332     if (work->codec_context != nullptr) {
333         astcenc_context_free(work->codec_context);
334         work->codec_context = nullptr;
335     }
336     work->data_out_ = nullptr;
337 }
338 
InitMem(AstcEncoder * work,TextureEncodeOptions param)339 static bool InitMem(AstcEncoder *work, TextureEncodeOptions param)
340 {
341     if (!work) {
342         return false;
343     }
344     work->swizzle_ = {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
345     work->image_.dim_x = static_cast<unsigned int>(param.width_);
346     work->image_.dim_y = static_cast<unsigned int>(param.height_);
347     work->image_.dim_z = 1;
348     if (param.privateProfile_ == HIGH_SPEED_PROFILE_HIGHBITS) {
349         work->image_.data_type = ASTCENC_TYPE_RGBA1010102;
350     } else {
351         work->image_.data_type = ASTCENC_TYPE_U8;
352     }
353     work->image_.dim_stride = static_cast<unsigned int>(param.stride_);
354     work->codec_context = nullptr;
355     work->image_.data = nullptr;
356     work->profile = ASTCENC_PRF_LDR_SRGB;
357 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
358     work->mse[R_COM] = work->mse[G_COM] = work->mse[B_COM] = work->mse[A_COM] = nullptr;
359     work->calQualityEnable = param.enableQualityCheck;
360     if (param.blocksNum <= 0) {
361         IMAGE_LOGE("[AstcCodec] InitMem blocksNum is invalid");
362         return false;
363     }
364     if (work->calQualityEnable) {
365         for (int i = R_COM; i < RGBA_COM; i++) {
366             work->mse[i] = static_cast<int32_t *>(calloc(param.blocksNum, sizeof(int32_t)));
367             if (!work->mse[i]) {
368                 IMAGE_LOGE("quality control calloc failed");
369                 return false;
370             }
371         }
372     }
373 #endif
374     work->image_.data = static_cast<void **>(malloc(sizeof(void*) * work->image_.dim_z));
375     if (!work->image_.data) {
376         return false;
377     }
378     return true;
379 }
380 
AstcSoftwareEncodeCore(TextureEncodeOptions & param,uint8_t * pixmapIn,uint8_t * astcBuffer)381 bool AstcCodec::AstcSoftwareEncodeCore(TextureEncodeOptions &param, uint8_t *pixmapIn, uint8_t *astcBuffer)
382 {
383     if ((pixmapIn == nullptr) || (astcBuffer == nullptr)) {
384         IMAGE_LOGE("pixmapIn or astcBuffer is nullptr");
385         return false;
386     }
387     AstcEncoder work;
388     if (!InitMem(&work, param)) {
389         FreeMem(&work);
390         return false;
391     }
392     if (InitAstcencConfig(&work, &param) != SUCCESS) {
393         IMAGE_LOGE("astc InitAstcencConfig failed");
394         FreeMem(&work);
395         return false;
396     }
397     work.image_.data[0] = pixmapIn;
398     work.data_out_ = astcBuffer;
399     if (GenAstcHeader(work.data_out_, work.image_, param) != SUCCESS) {
400         IMAGE_LOGE("astc GenAstcHeader failed");
401         FreeMem(&work);
402         return false;
403     }
404     work.error_ = astcenc_compress_image(work.codec_context, &work.image_, &work.swizzle_,
405         work.data_out_ + TEXTURE_HEAD_BYTES, param.astcBytes - TEXTURE_HEAD_BYTES,
406 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
407         work.calQualityEnable, work.mse,
408 #endif
409         0);
410 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
411     if ((ASTCENC_SUCCESS != work.error_) ||
412         (work.calQualityEnable && !CheckQuality(work.mse, param.blocksNum, param.blockX_ * param.blockY_))) {
413 #else
414     if (ASTCENC_SUCCESS != work.error_) {
415 #endif
416         IMAGE_LOGE("astc compress failed");
417         FreeMem(&work);
418         return false;
419     }
420     FreeMem(&work);
421     return true;
422 }
423 
424 static QualityProfile GetAstcQuality(int32_t quality)
425 {
426     QualityProfile privateProfile;
427     switch (quality) {
428         case HIGH_SPEED_PROFILE_MAP_QUALITY:
429             privateProfile = HIGH_SPEED_PROFILE;
430             break;
431         default:
432             privateProfile = HIGH_QUALITY_PROFILE;
433             break;
434     }
435     return privateProfile;
436 }
437 
438 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
439 bool AstcCodec::TryAstcEncBasedOnCl(TextureEncodeOptions &param, uint8_t *inData,
440     uint8_t *buffer, const std::string &clBinPath)
441 {
442     ClAstcHandle *astcClEncoder = nullptr;
443     bool invalidPara = (inData == nullptr) || (buffer == nullptr);
444     if (invalidPara) {
445         IMAGE_LOGE("astc Please check TryAstcEncBasedOnCl input!");
446         return false;
447     }
448     if (AstcClCreate(&astcClEncoder, clBinPath) != CL_ASTC_ENC_SUCCESS) {
449         IMAGE_LOGE("astc AstcClCreate failed!");
450         return false;
451     }
452     ClAstcImageOption imageIn;
453     if (AstcClFillImage(&imageIn, inData, param.stride_, param.width_, param.height_) != CL_ASTC_ENC_SUCCESS) {
454         IMAGE_LOGE("astc AstcClFillImage failed!");
455         AstcClClose(astcClEncoder);
456         return false;
457     }
458     if (AstcClEncImage(astcClEncoder, &imageIn, buffer) != CL_ASTC_ENC_SUCCESS) {
459         IMAGE_LOGE("astc AstcClEncImage failed!");
460         AstcClClose(astcClEncoder);
461         return false;
462     }
463     if (AstcClClose(astcClEncoder) != CL_ASTC_ENC_SUCCESS) {
464         IMAGE_LOGE("astc AstcClClose failed!");
465         return false;
466     }
467     return true;
468 }
469 #endif
470 
471 #ifdef SUT_ENCODE_ENABLE
472 static bool FillAstcSutInfo(AstcInInfo &astcInfo, SutOutInfo &sutInfo, TextureEncodeOptions &param,
473     uint8_t *astcBuffer)
474 {
475     astcInfo.astcBuf = astcBuffer;
476     astcInfo.astcBytes = param.astcBytes;
477     astcInfo.expandNums = param.expandNums;
478     int32_t expandTotalBytes = 0;
479     for (uint8_t idx = 0; idx < astcInfo.expandNums; idx++) {
480         astcInfo.expandInfoType[idx] = idx;
481         astcInfo.expandInfoBytes[idx] = param.extInfoBytes;
482         astcInfo.expandInfoBuf[idx] = param.extInfoBuf;
483         expandTotalBytes += sizeof(uint8_t) + sizeof(int32_t) + param.extInfoBytes;
484     }
485     sutInfo.sutCapacity = astcInfo.astcBytes + EXPAND_SIZE_BYTES_ENC + expandTotalBytes;
486     sutInfo.sutBuf = static_cast<uint8_t *>(malloc(sutInfo.sutCapacity));
487     if (sutInfo.sutBuf == nullptr) {
488         IMAGE_LOGD("astcenc sutInfo.sutBuf malloc failed!");
489         return false;
490     }
491     return true;
492 }
493 
494 static bool SutEncode(TextureEncodeOptions &param, uint8_t *astcBuffer)
495 {
496     bool invalidSutEnc = !g_sutEncSoManager.LoadSutEncSo() || g_sutEncSoManager.sutEncSoEncFunc_ == nullptr;
497     if (invalidSutEnc) {
498         IMAGE_LOGE("sut enc so dlopen failed or sutEncSoEncFunc_ is nullptr!");
499         param.sutProfile = SutProfile::SKIP_SUT;
500         return true;
501     }
502     AstcInInfo astcInfo = {0};
503     SutOutInfo sutInfo = {0};
504     int32_t ret = memset_s(&astcInfo, sizeof(AstcInInfo), 0, sizeof(AstcInInfo));
505     if (ret != 0) {
506         IMAGE_LOGE("AstcInInfo memset failed!");
507         return false;
508     }
509     ret = memset_s(&sutInfo, sizeof(SutOutInfo), 0, sizeof(SutOutInfo));
510     if (ret != 0) {
511         IMAGE_LOGE("SutOutInfo memset failed!");
512         return false;
513     }
514 
515     if (!FillAstcSutInfo(astcInfo, sutInfo, param, astcBuffer)) {
516         IMAGE_LOGE("FillAstcSutInfo fail");
517         return false;
518     }
519     if (!g_sutEncSoManager.sutEncSoEncFunc_(astcInfo, sutInfo,
520         static_cast<uint32_t>(param.sutProfile))) {
521         IMAGE_LOGE("astc g_sutEncSoEncFunc failed!");
522         free(sutInfo.sutBuf);
523         return false;
524     }
525     if (memcpy_s(astcBuffer, param.astcBytes, sutInfo.sutBuf, sutInfo.sutBytes) < 0) {
526         IMAGE_LOGE("sut sutbuffer is failed to be copied to astcBuffer!");
527         free(sutInfo.sutBuf);
528         return false;
529     }
530     free(sutInfo.sutBuf);
531     param.outIsSut = true;
532     param.astcBytes = sutInfo.sutBytes;
533     param.sutBytes = param.astcBytes;
534     return true;
535 }
536 
537 bool AstcCodec::TryTextureSuperCompress(TextureEncodeOptions &param, uint8_t *astcBuffer)
538 {
539     bool skipSutEnc = (param.sutProfile == SutProfile::SKIP_SUT) ||
540         ((!param.hardwareFlag) && (param.privateProfile_ != HIGH_SPEED_PROFILE)) ||
541         (param.blockX_ != DEFAULT_DIM && param.blockY_ != DEFAULT_DIM);
542     switch (param.textureEncodeType) {
543         case TextureEncodeType::HDR_ASTC_4X4:
544         case TextureEncodeType::SDR_ASTC_4X4:
545             param.sutProfile = SutProfile::SKIP_SUT;
546             IMAGE_LOGD("sdr_astc_4x4 is not suit to be compressed to sut!");
547             return true;
548         case TextureEncodeType::ASTC:
549             if (skipSutEnc) {
550                 IMAGE_LOGD("astc is not suit to be compressed to sut!");
551                 param.sutProfile = SutProfile::SKIP_SUT;
552                 return true;
553             }
554             break;
555         case TextureEncodeType::SDR_SUT_SUPERFAST_4X4:
556             break;
557         default:
558             IMAGE_LOGE("TextureEncodeType is failed");
559             return false;
560     }
561 
562     return SutEncode(param, astcBuffer);
563 }
564 #endif
565 
566 static bool GetSutSdrProfile(PlEncodeOptions &astcOpts,
567     SutProfile &sutProfile, QualityProfile &privateProfile)
568 {
569     auto sutNode = SUT_FORMAT_MAP.find(astcOpts.format);
570     if (sutNode != SUT_FORMAT_MAP.end()) {
571         auto [sutQ, sutP, astcP] = sutNode->second;
572         if (sutQ != astcOpts.quality) {
573             IMAGE_LOGE("GetSutSdrProfile failed %{public}d is invalid!", astcOpts.quality);
574             return false;
575         }
576         sutProfile = sutP;
577         privateProfile = astcP;
578         return true;
579     }
580     return false;
581 }
582 
583 static bool GetAstcProfile(PlEncodeOptions &astcOpts, QualityProfile &privateProfile)
584 {
585     auto astcNode = ASTC_FORMAT_MAP.find(astcOpts.format);
586     if (astcNode != ASTC_FORMAT_MAP.end()) {
587         const auto &qualityMap = astcNode->second;
588         auto qualityNode = qualityMap.find(astcOpts.quality);
589         if (qualityNode != qualityMap.end()) {
590             privateProfile = qualityNode->second;
591             return true;
592         }
593         IMAGE_LOGE("GetAstcProfile failed %{public}d is invalid!", astcOpts.quality);
594         return false;
595     }
596     return false;
597 }
598 
599 bool CheckParamBlockSize(TextureEncodeOptions &param)
600 {
601     if ((param.width_ <= 0) || (param.height_ <= 0)) {
602         IMAGE_LOGE("CheckAstcEncInput width <= 0 or height <= 0 !");
603         return false;
604     }
605     if ((param.width_ > WIDTH_MAX_ASTC) || (param.height_ > HEIGHT_MAX_ASTC)) {
606         IMAGE_LOGE("CheckAstcEncInput width %{public}d height %{public}d out of range %{public}d x %{public}d!",
607                    param.width_, param.height_, WIDTH_MAX_ASTC, HEIGHT_MAX_ASTC);
608         return false;
609     }
610     if ((param.blockX_ < DEFAULT_DIM) || (param.blockY_ < DEFAULT_DIM)) {
611         IMAGE_LOGE("CheckAstcEncInput block %{public}d x %{public}d < 4 x 4!", param.blockX_, param.blockY_);
612         return false;
613     }
614     if ((param.blockX_ > MAX_DIM) || (param.blockY_ > MAX_DIM)) {
615         IMAGE_LOGE("CheckAstcEncInput block %{public}d x %{public}d > 12 x 12!", param.blockX_, param.blockY_);
616         return false;
617     }
618     return true;
619 }
620 
621 static bool InitAstcEncPara(TextureEncodeOptions &param,
622     int32_t width, int32_t height, int32_t stride, PlEncodeOptions &astcOpts)
623 {
624     SutProfile sutProfile;
625     QualityProfile qualityProfile;
626     if (astcOpts.format == "image/sdr_sut_superfast_4x4") { // sut sdr encode
627         if (!GetSutSdrProfile(astcOpts, sutProfile, qualityProfile)) {
628             IMAGE_LOGE("InitAstcEncPara GetSutSdrProfile failed");
629             return false;
630         }
631         param.textureEncodeType = TextureEncodeType::SDR_SUT_SUPERFAST_4X4;
632     } else if (astcOpts.format == "image/sdr_astc_4x4") { // astc sdr encode
633         if (!GetAstcProfile(astcOpts, qualityProfile)) {
634             IMAGE_LOGE("InitAstcEncPara GetAstcProfile failed");
635             return false;
636         }
637         sutProfile = SutProfile::SKIP_SUT;
638         param.textureEncodeType = TextureEncodeType::SDR_ASTC_4X4;
639     } else if (astcOpts.format == "image/hdr_astc_4x4") { // astc hdr encode
640         if (!GetAstcProfile(astcOpts, qualityProfile)) {
641             IMAGE_LOGE("InitAstcEncPara GetAstcProfile failed");
642             return false;
643         }
644         sutProfile = SutProfile::SKIP_SUT;
645         param.textureEncodeType = TextureEncodeType::HDR_ASTC_4X4;
646     } else if (astcOpts.format.find("image/astc") == 0) { // old astc encode
647         qualityProfile = GetAstcQuality(astcOpts.quality);
648         sutProfile = SutProfile::SKIP_SUT;
649         param.textureEncodeType = TextureEncodeType::ASTC;
650     } else {
651         IMAGE_LOGE("InitAstcEncPara format invalidation:%{public}s", astcOpts.format.c_str());
652         return false;
653     }
654     param.enableQualityCheck = false;
655     param.hardwareFlag = false;
656     param.sutProfile = sutProfile;
657     param.width_ = width;
658     param.height_ = height;
659     param.stride_ = stride;
660     param.privateProfile_ = qualityProfile;
661     param.outIsSut = false;
662     extractDimensions(astcOpts.format, param);
663     if (!CheckParamBlockSize(param)) { // DEFAULT_DIM = 4
664         IMAGE_LOGE("InitAstcEncPara failed %{public}dx%{public}d is invalid!", param.blockX_, param.blockY_);
665         return false;
666     }
667     param.blocksNum = ((param.width_ + param.blockX_ - 1) / param.blockX_) *
668         ((param.height_ + param.blockY_ - 1) / param.blockY_);
669     param.astcBytes = param.blocksNum * TEXTURE_HEAD_BYTES + TEXTURE_HEAD_BYTES;
670     return true;
671 }
672 
673 static void FillAstcEncCheckInfo(AstcEncCheckInfo &checkInfo, Media::PixelMap* astcPixelMap, uint32_t astcBufferSize,
674                                  uint32_t extendInfoSize, uint32_t extendBufferSize)
675 {
676     checkInfo.pixmapInSize = astcPixelMap->GetAllocationByteCount();
677     checkInfo.pixmapFormat = astcPixelMap->GetPixelFormat();
678     checkInfo.astcBufferSize = astcBufferSize;
679     checkInfo.extendInfoSize = extendInfoSize;
680     checkInfo.extendBufferSize = extendBufferSize;
681 }
682 
683 static bool CheckAstcEncInput(TextureEncodeOptions &param, AstcEncCheckInfo checkInfo)
684 {
685     uint32_t pixmapStride = static_cast<uint32_t>(param.stride_) << RGBA_BYTES_PIXEL_LOG2;
686     if ((param.width_ <= 0) || (param.height_ <= 0) || (param.stride_ < param.width_)) {
687         IMAGE_LOGE("CheckAstcEncInput width <= 0 or height <= 0 or stride < width!");
688         return false;
689     }
690     if ((param.width_ > WIDTH_MAX_ASTC) || (param.height_ > HEIGHT_MAX_ASTC)) {
691         IMAGE_LOGE("CheckAstcEncInput width %{public}d height %{public}d out of range %{public}d x %{public}d!",
692                    param.width_, param.height_, WIDTH_MAX_ASTC, HEIGHT_MAX_ASTC);
693         return false;
694     }
695     uint64_t allocByteCount = static_cast<uint32_t>(param.height_) * pixmapStride;
696     if (allocByteCount > std::numeric_limits<uint32_t>::max()) {
697         IMAGE_LOGE("CheckAstcEncInput height %{public}d stride %{public}d overflow!",
698                     param.height_, pixmapStride);
699         return false;
700     }
701     if (checkInfo.pixmapInSize < static_cast<uint32_t>(allocByteCount)) {
702         IMAGE_LOGE("CheckAstcEncInput pixmapInSize %{public}d not enough for height %{public}d stride %{public}d!",
703                    checkInfo.pixmapInSize, param.height_, pixmapStride);
704         return false;
705     }
706     if ((param.blockX_ < DEFAULT_DIM) || (param.blockY_ < DEFAULT_DIM)) {
707         IMAGE_LOGE("CheckAstcEncInput block %{public}d x %{public}d < 4 x 4!", param.blockX_, param.blockY_);
708         return false;
709     }
710     if ((param.blockX_ > MAX_DIM) || (param.blockY_ > MAX_DIM)) {
711         IMAGE_LOGE("CheckAstcEncInput block %{public}d x %{public}d > 12 x 12!", param.blockX_, param.blockY_);
712         return false;
713     }
714     const bool isAstcHdr = (param.textureEncodeType == TextureEncodeType::HDR_ASTC_4X4);
715     const PixelFormat expectedFormat = isAstcHdr ? PixelFormat::RGBA_1010102 : PixelFormat::RGBA_8888;
716     if (checkInfo.pixmapFormat != expectedFormat) {
717         IMAGE_LOGE("CheckAstcEncInput textureEncodeType: %{public}d, pixelFormat %{public}d must be RGBA!",
718             param.textureEncodeType, checkInfo.pixmapFormat);
719         return false;
720     }
721     uint32_t packSize = static_cast<uint32_t>(param.astcBytes) + checkInfo.extendInfoSize + checkInfo.extendBufferSize;
722     if (checkInfo.astcBufferSize < packSize) {
723         IMAGE_LOGE("CheckAstcEncInput astcBufferSize %{public}d not enough for %{public}d!",
724                    checkInfo.astcBufferSize, packSize);
725         return false;
726     }
727     return true;
728 }
729 
730 uint32_t AstcCodec::AstcSoftwareEncode(TextureEncodeOptions &param, bool enableQualityCheck,
731     int32_t blocksNum, uint8_t *outBuffer, int32_t outSize)
732 {
733     ImageInfo imageInfo;
734     astcPixelMap_->GetImageInfo(imageInfo);
735     uint8_t *pixmapIn = static_cast<uint8_t *>(astcPixelMap_->GetWritablePixels());
736     uint32_t stride = static_cast<uint32_t>(astcPixelMap_->GetRowStride()) >> RGBA_BYTES_PIXEL_LOG2;
737     if (!InitAstcEncPara(param, imageInfo.size.width, imageInfo.size.height, static_cast<int32_t>(stride), astcOpts_)) {
738         IMAGE_LOGE("InitAstcEncPara failed");
739         return ERROR;
740     }
741     param.enableQualityCheck = enableQualityCheck;
742     AstcEncCheckInfo checkInfo;
743     FillAstcEncCheckInfo(checkInfo, astcPixelMap_, param.astcBytes, 0, 0);
744     if (!CheckAstcEncInput(param, checkInfo)) {
745         IMAGE_LOGE("CheckAstcEncInput failed");
746         return ERROR;
747     }
748     if (!AstcSoftwareEncodeCore(param, pixmapIn, outBuffer)) {
749         IMAGE_LOGE("AstcSoftwareEncodeCore failed");
750         return ERROR;
751     }
752     return SUCCESS;
753 }
754 
755 static bool AstcEncProcess(TextureEncodeOptions &param, uint8_t *pixmapIn, uint8_t *astcBuffer,
756                            AstcEncCheckInfo checkInfo)
757 {
758     if (!CheckAstcEncInput(param, checkInfo)) {
759         IMAGE_LOGE("CheckAstcEncInput failed");
760         return false;
761     }
762 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
763     bool openClEnc = param.width_ >= WIDTH_CL_THRESHOLD && param.height_ >= HEIGHT_CL_THRESHOLD &&
764         param.privateProfile_ == QualityProfile::HIGH_SPEED_PROFILE;
765     bool enableClEnc = openClEnc && ImageSystemProperties::GetGenThumbWithGpu() &&
766         (param.blockX_ == DEFAULT_DIM) && (param.blockY_ == DEFAULT_DIM); // HardWare only support 4x4 now
767     if (enableClEnc) {
768         IMAGE_LOGI("astc hardware encode begin");
769         std::string clBinPath = "/sys_prod/etc/graphic/AstcEncShader_ALN-AL00.bin";
770         if (!CheckClBinIsExistWithLock(clBinPath)) {
771             clBinPath = "/data/storage/el1/base/AstcEncShader.bin";
772         }
773         param.hardwareFlag = AstcCodec::TryAstcEncBasedOnCl(param, pixmapIn, astcBuffer, clBinPath);
774     }
775 #endif
776     if (!param.hardwareFlag) {
777         if (!AstcCodec::AstcSoftwareEncodeCore(param, pixmapIn, astcBuffer)) {
778             IMAGE_LOGE("AstcSoftwareEncodeCore failed");
779             return false;
780         }
781         IMAGE_LOGD("astc software encode success!");
782     }
783     return true;
784 }
785 
786 void AstcCodec::InitTextureEncodeOptions(TextureEncodeOptions &param, uint8_t &colorData)
787 {
788     param.expandNums = 1;
789     param.extInfoBytes = 1;
790 #ifdef IMAGE_COLORSPACE_FLAG
791     colorData = static_cast<uint8_t>(astcPixelMap_->InnerGetGrColorSpace().GetColorSpaceName());
792 #else
793     colorData = 0;
794 #endif
795 }
796 
797 bool AstcCodec::IsAstcEnc(Media::ImageInfo &info, uint8_t* pixelmapIn, TextureEncodeOptions &param,
798     AstcExtendInfo &extendInfo)
799 {
800     int32_t bufferSize = astcPixelMap_->GetCapacity();
801     if (bufferSize <= 0) {
802         IMAGE_LOGE("buffer size is less and equal than zero in packing");
803         return false;
804     }
805     if (pixelmapIn == nullptr) {
806         IMAGE_LOGE("pixelmap data is nullptr in packing");
807         return false;
808     }
809     if (info.pixelFormat == PixelFormat::ASTC_4x4 && astcOpts_.format == "image/sdr_astc_4x4") {
810         if (!astcOutput_->Write(pixelmapIn, bufferSize)) {
811             IMAGE_LOGE("fail to write to astcout");
812             return false;
813         }
814         return true;
815     }
816     if (info.pixelFormat == PixelFormat::ASTC_4x4 && astcOpts_.format == "image/sdr_sut_superfast_4x4") {
817         uint8_t *astcBuffer = static_cast<uint8_t *>(malloc(bufferSize));
818         if (astcBuffer == nullptr) {
819             IMAGE_LOGE("Buffer malloc failed in packing");
820             return false;
821         }
822         if (memcpy_s(astcBuffer, bufferSize, pixelmapIn, bufferSize) < 0) {
823             IMAGE_LOGE("Copy data failed in packing");
824             free(astcBuffer);
825             return false;
826         }
827         if (!TryEncSUT(param, astcBuffer, extendInfo)) {
828             IMAGE_LOGE("Astc encode sut failed in packing");
829             return false;
830         }
831         bufferSize = static_cast<uint32_t>(param.sutBytes);
832         if (!astcOutput_->Write(astcBuffer, bufferSize)) {
833             IMAGE_LOGE("Write to astcout failed in packing");
834             free(astcBuffer);
835             return false;
836         }
837         free(astcBuffer);
838         return true;
839     }
840     IMAGE_LOGE("Does not support packing format");
841     return false;
842 }
843 
844 bool AstcCodec::InitBeforeAstcEncode(ImageInfo &imageInfo, TextureEncodeOptions &param, uint8_t &colorData,
845     uint8_t **pixmapIn, uint32_t &stride)
846 {
847     if (astcPixelMap_ == nullptr || astcOutput_ == nullptr) {
848         return false;
849     }
850     astcPixelMap_->GetImageInfo(imageInfo);
851     InitTextureEncodeOptions(param, colorData);
852     param.extInfoBuf = &colorData;
853     *pixmapIn = const_cast<uint8_t *>(astcPixelMap_->GetPixels());
854     stride = static_cast<uint32_t>(astcPixelMap_->GetRowStride()) >> RGBA_BYTES_PIXEL_LOG2;
855     if (!InitAstcEncPara(param, imageInfo.size.width, imageInfo.size.height, static_cast<int32_t>(stride), astcOpts_)) {
856         IMAGE_LOGE("InitAstcEncPara failed");
857         return false;
858     }
859     return true;
860 }
861 
862 bool AstcCodec::TryEncSUT(TextureEncodeOptions &param, uint8_t* astcBuffer, AstcExtendInfo &extendInfo)
863 {
864 #ifdef SUT_ENCODE_ENABLE
865     if (!TryTextureSuperCompress(param, astcBuffer)) {
866         IMAGE_LOGE("astc TryTextureSuperCompress failed!");
867         ReleaseExtendInfoMemory(extendInfo);
868         free(astcBuffer);
869         return false;
870     }
871     return true;
872 #else
873     return true;
874 #endif
875 }
876 
877 uint32_t AstcCodec::ASTCEncode() __attribute__((no_sanitize("cfi")))
878 {
879     ImageInfo imageInfo;
880     TextureEncodeOptions param;
881     uint8_t colorData;
882     uint8_t *pixmapIn = nullptr;
883     uint32_t stride = 0;
884     if (!InitBeforeAstcEncode(imageInfo, param, colorData, &pixmapIn, stride)) {
885         return ERROR;
886     }
887     ImageTrace("[AstcCodec] ASTCEncode Size: %d, %d", imageInfo.size.width, imageInfo.size.height);
888     AstcExtendInfo extendInfo = {0};
889     if (!InitAstcExtendInfo(extendInfo)) {
890         return ERROR;
891     }
892     uint32_t packSize = static_cast<uint32_t>(param.astcBytes) + extendInfo.extendBufferSumBytes + ASTC_NUM_4;
893     if (astcPixelMap_->IsAstc()) {
894         if (!IsAstcEnc(imageInfo, pixmapIn, param, extendInfo)) {
895             return ERROR;
896         }
897         return SUCCESS;
898     }
899     uint8_t *astcBuffer = static_cast<uint8_t *>(malloc(packSize));
900     if (astcBuffer == nullptr) {
901         ReleaseExtendInfoMemory(extendInfo);
902         return ERROR;
903     }
904     AstcEncCheckInfo checkInfo;
905     FillAstcEncCheckInfo(checkInfo, astcPixelMap_, packSize, ASTC_NUM_4, extendInfo.extendBufferSumBytes);
906     if (!AstcEncProcess(param, pixmapIn, astcBuffer, checkInfo)) {
907         IMAGE_LOGE("astc AstcEncProcess failed!");
908         ReleaseExtendInfoMemory(extendInfo);
909         free(astcBuffer);
910         return ERROR;
911     }
912     if (!TryEncSUT(param, astcBuffer, extendInfo)) {
913         return ERROR;
914     }
915     if (!param.outIsSut) { // only support astc for color space
916         WriteAstcExtendInfo(astcBuffer, static_cast<uint32_t>(param.astcBytes), extendInfo);
917     } else {
918         packSize = static_cast<uint32_t>(param.sutBytes);
919     }
920     ReleaseExtendInfoMemory(extendInfo);
921     if (!astcOutput_->Write(astcBuffer, packSize)) {
922         free(astcBuffer);
923         return ERROR;
924     }
925     free(astcBuffer);
926     IMAGE_LOGD("astcenc end: %{public}dx%{public}d, GpuFlag %{public}d, sut%{public}d",
927         imageInfo.size.width, imageInfo.size.height, param.hardwareFlag, param.sutProfile);
928     astcOutput_->SetOffset(packSize);
929     return SUCCESS;
930 }
931 
932 bool AllocMemForExtInfo(AstcExtendInfo &extendInfo, uint8_t idx)
933 {
934     auto AllocAndCopy = [&](size_t dataSize, const void* srcData, const char* logTag) -> bool {
935         extendInfo.extendInfoLength[idx] = static_cast<uint32_t>(dataSize);
936         extendInfo.extendBufferSumBytes += ASTC_EXTEND_INFO_TYPE_LENGTH +
937                                           ASTC_EXTEND_INFO_LENGTH_LENGTH +
938                                           extendInfo.extendInfoLength[idx];
939         if (dataSize <= 0) {
940             return false;
941         }
942         extendInfo.extendInfoValue[idx] = static_cast<uint8_t*>(malloc(dataSize));
943         if (extendInfo.extendInfoValue[idx] == nullptr) {
944             IMAGE_LOGE("[AstcCodec] %s malloc failed!", logTag);
945             return false;
946         }
947         if (srcData && memcpy_s(extendInfo.extendInfoValue[idx], dataSize,
948                                srcData, dataSize) != 0) {
949             IMAGE_LOGE("[AstcCodec] %s memcpy failed!", logTag);
950             return false;
951         }
952         return true;
953     };
954     AstcExtendInfoType type = static_cast<AstcExtendInfoType>(idx);
955     switch (type) {
956         case AstcExtendInfoType::COLOR_SPACE:
957         case AstcExtendInfoType::PIXEL_FORMAT:
958             // These types only require memory allocation and do not require data initialization
959             return AllocAndCopy(ASTC_EXTEND_INFO_COLOR_SPACE_VALUE_LENGTH,
960                                nullptr, type == AstcExtendInfoType::COLOR_SPACE ?
961                                "SetColorSpaceInfo" : "SetPixelFormatInfo");
962         case AstcExtendInfoType::HDR_METADATA_TYPE:
963             return AllocAndCopy(extendInfo.astcMetadata.hdrMetadataTypeVec.size(),
964                                extendInfo.astcMetadata.hdrMetadataTypeVec.data(),
965                                "SetHdrMetadataType");
966         case AstcExtendInfoType::HDR_COLORSPACE_INFO:
967             return AllocAndCopy(extendInfo.astcMetadata.colorSpaceInfoVec.size(),
968                                extendInfo.astcMetadata.colorSpaceInfoVec.data(),
969                                "SetHdrColorSpaceInfo");
970         case AstcExtendInfoType::HDR_STATIC_DATA:
971             return AllocAndCopy(extendInfo.astcMetadata.staticData.size(),
972                                extendInfo.astcMetadata.staticData.data(),
973                                "SetHdrStaticData");
974         case AstcExtendInfoType::HDR_DYNAMIC_DATA:
975             return AllocAndCopy(extendInfo.astcMetadata.dynamicData.size(),
976                                extendInfo.astcMetadata.dynamicData.data(),
977                                "SetHdrDynamicData");
978         default:
979             IMAGE_LOGE("[AstcCodec] Unknown extend info type: %d", static_cast<int>(type));
980             return false;
981     }
982 }
983 
984 bool AstcCodec::FillMetaData(AstcExtendInfo &extendInfo, PixelMap *astcPixelMap)
985 {
986     if (astcPixelMap == nullptr || astcPixelMap->GetFd() == nullptr ||
987         astcPixelMap->GetAllocatorType() != AllocatorType::DMA_ALLOC) {
988         IMAGE_LOGE("[AstcCodec] astcPixelMap is invalid!");
989         return false;
990     }
991     if (!astcPixelMap->IsHdr()) {
992         return false; // No need to fill metadata for SDR
993     }
994     sptr<SurfaceBuffer> source(reinterpret_cast<SurfaceBuffer *>(astcPixelMap->GetFd()));
995     bool baseMetadataSuccess = true;
996     bool vpeMetadataSuccess = true;  // Track static/dynamic metadata status separately
997 
998     auto CheckResult = [&](GSError status, const char* metadataName) {
999         if (status != GSERROR_OK) {
1000             IMAGE_LOGE("[AstcCodec] GetMetadata %{public}s failed: %{public}d", metadataName, status);
1001             baseMetadataSuccess = false;
1002         }
1003     };
1004     CheckResult(source->GetMetadata(ATTRKEY_HDR_METADATA_TYPE,
1005                extendInfo.astcMetadata.hdrMetadataTypeVec), "ATTRKEY_HDR_METADATA_TYPE");
1006     CheckResult(source->GetMetadata(ATTRKEY_COLORSPACE_INFO,
1007                extendInfo.astcMetadata.colorSpaceInfoVec), "ATTRKEY_COLORSPACE_INFO");
1008     // Check if required metadata is filled
1009     if (!baseMetadataSuccess) {
1010         IMAGE_LOGE("[AstcCodec] Critical base metadata missing!");
1011         return false;
1012     }
1013 
1014     auto CheckVpeResult = [&](bool result, const char* metadataName, auto& dataVec) {
1015         if (!result || dataVec.empty()) {
1016             IMAGE_LOGE("[AstcCodec] %{public}s failed or empty", metadataName);
1017             vpeMetadataSuccess = false;  // Only marking failures does not affect the basic metadata status
1018         }
1019     };
1020     // Check static/dynamic metadata (non critical)
1021     CheckVpeResult(VpeUtils::GetSbStaticMetadata(source, extendInfo.astcMetadata.staticData),
1022                   "GetSbStaticMetadata", extendInfo.astcMetadata.staticData);
1023     CheckVpeResult(VpeUtils::GetSbDynamicMetadata(source, extendInfo.astcMetadata.dynamicData),
1024                   "GetSbDynamicMetadata", extendInfo.astcMetadata.dynamicData);
1025 
1026     // Dynamically set extendNums based on VP metadata results
1027     extendInfo.extendNums = vpeMetadataSuccess ? ASTC_EXTEND_INFO_TLV_NUM_6 : ASTC_EXTEND_INFO_TLV_NUM_4;
1028 
1029     IMAGE_LOGI("[AstcCodec] HDR metadata: base=%{public}s, VP=%{public}s, using extendNums=%{public}d",
1030                baseMetadataSuccess ? "success" : "fail",
1031                vpeMetadataSuccess ? "success" : "fail",
1032                extendInfo.extendNums);
1033     return true;
1034 }
1035 
1036 bool AstcCodec::InitAstcExtendInfo(AstcExtendInfo &extendInfo)
1037 {
1038     if (memset_s(&extendInfo, sizeof(AstcExtendInfo), 0, sizeof(AstcExtendInfo)) != 0) {
1039         IMAGE_LOGE("[AstcCodec] memset extendInfo failed!");
1040         return false;
1041     }
1042     extendInfo.extendNums = astcPixelMap_->IsHdr() ? ASTC_EXTEND_INFO_TLV_NUM_6 : ASTC_EXTEND_INFO_TLV_NUM;
1043     extendInfo.extendBufferSumBytes = 0;
1044     if (astcPixelMap_->IsHdr()) {
1045         if (!FillMetaData(extendInfo, astcPixelMap_)) {
1046             IMAGE_LOGE("[AstcCodec] FillMetaData failed!");
1047             return false;
1048         }
1049     }
1050     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
1051         if (!AllocMemForExtInfo(extendInfo, idx)) {
1052             ReleaseExtendInfoMemory(extendInfo);
1053             IMAGE_LOGE("[AstcCodec] AllocMemForExtInfo failed!");
1054             return false;
1055         }
1056     }
1057     return true;
1058 }
1059 
1060 void AstcCodec::ReleaseExtendInfoMemory(AstcExtendInfo &extendInfo)
1061 {
1062     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
1063         if (extendInfo.extendInfoValue[idx] != nullptr) {
1064             free(extendInfo.extendInfoValue[idx]);
1065             extendInfo.extendInfoValue[idx] = nullptr;
1066         }
1067     }
1068 }
1069 
1070 static void FillDataSize(uint8_t *buf, uint32_t bytes)
1071 {
1072     *buf++ = (bytes) & MASKBITS_FOR_8BITS;
1073     *buf++ = (bytes >> UINT32_1TH_BYTES) & MASKBITS_FOR_8BITS;
1074     *buf++ = (bytes >> UINT32_2TH_BYTES) & MASKBITS_FOR_8BITS;
1075     *buf++ = (bytes >> UINT32_3TH_BYTES) & MASKBITS_FOR_8BITS;
1076 }
1077 
1078 void AstcCodec::WriteAstcExtendInfo(uint8_t *buffer, uint32_t offset, AstcExtendInfo &extendInfo)
1079 {
1080     uint8_t* offsetBuffer = buffer + offset;
1081     FillDataSize(offsetBuffer, extendInfo.extendBufferSumBytes);
1082     offsetBuffer += ASTC_EXTEND_INFO_SIZE_DEFINITION_LENGTH;
1083 #ifdef IMAGE_COLORSPACE_FLAG
1084     ColorManager::ColorSpace colorspace = astcPixelMap_->InnerGetGrColorSpace();
1085     ColorManager::ColorSpaceName csName = colorspace.GetColorSpaceName();
1086 #endif
1087     PixelFormat pixelFormat = astcPixelMap_->GetPixelFormat();
1088     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
1089         *offsetBuffer++ = idx;
1090         FillDataSize(offsetBuffer, extendInfo.extendInfoLength[idx]);
1091         offsetBuffer += ASTC_EXTEND_INFO_LENGTH_LENGTH;
1092         AstcExtendInfoType type = static_cast<AstcExtendInfoType>(idx);
1093         switch (type) {
1094             case AstcExtendInfoType::COLOR_SPACE:
1095 #ifdef IMAGE_COLORSPACE_FLAG
1096                 *offsetBuffer = static_cast<uint8_t>(csName);
1097 #else
1098                 *offsetBuffer = 0;
1099 #endif
1100                 offsetBuffer += ASTC_EXTEND_INFO_COLOR_SPACE_VALUE_LENGTH;
1101                 break;
1102             case AstcExtendInfoType::PIXEL_FORMAT:
1103                 *offsetBuffer = static_cast<uint8_t>(pixelFormat);
1104                 offsetBuffer += ASTC_EXTEND_INFO_PIXEL_FORMAT_VALUE_LENGTH;
1105                 break;
1106             case AstcExtendInfoType::HDR_METADATA_TYPE:
1107             case AstcExtendInfoType::HDR_COLORSPACE_INFO:
1108             case AstcExtendInfoType::HDR_STATIC_DATA:
1109             case AstcExtendInfoType::HDR_DYNAMIC_DATA:
1110                 if (extendInfo.extendInfoValue[idx] == nullptr||
1111                     extendInfo.extendInfoLength[idx] <= 0 ||
1112                     memcpy_s(offsetBuffer, extendInfo.extendInfoLength[idx],
1113                     extendInfo.extendInfoValue[idx], extendInfo.extendInfoLength[idx]) != 0) {
1114                     IMAGE_LOGE("[AstcCodec] WriteAstcExtendInfo memcpy failed!");
1115                     return;
1116                 }
1117                 offsetBuffer += extendInfo.extendInfoLength[idx];
1118                 break;
1119             default:
1120                 return;
1121         }
1122     }
1123 }
1124 } // namespace ImagePlugin
1125 } // namespace OHOS
1126