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