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_system_properties.h"
25 #include "securec.h"
26 #include "media_errors.h"
27 
28 #undef LOG_DOMAIN
29 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
30 
31 #undef LOG_TAG
32 #define LOG_TAG "AstcCodec"
33 
34 namespace OHOS {
35 namespace ImagePlugin {
36 using namespace Media;
37 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
38 using namespace AstcEncBasedCl;
39 #endif
40 constexpr uint8_t TEXTURE_HEAD_BYTES = 16;
41 constexpr uint8_t ASTC_MASK = 0xFF;
42 constexpr uint8_t ASTC_NUM_8 = 8;
43 constexpr uint8_t ASTC_HEADER_SIZE = 16;
44 constexpr uint8_t ASTC_NUM_24 = 24;
45 static const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
46 constexpr uint8_t DEFAULT_DIM = 4;
47 constexpr uint8_t HIGH_SPEED_PROFILE_MAP_QUALITY = 20; // quality level is 20 for thumbnail
48 constexpr uint8_t RGBA_BYTES_PIXEL_LOG2 = 2;
49 constexpr uint8_t MASKBITS_FOR_8BITS = 255;
50 constexpr uint8_t UINT32_1TH_BYTES = 8;
51 constexpr uint8_t UINT32_2TH_BYTES = 16;
52 constexpr uint8_t UINT32_3TH_BYTES = 24;
53 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
54 constexpr int32_t WIDTH_CL_THRESHOLD = 256;
55 constexpr int32_t HEIGHT_CL_THRESHOLD = 256;
56 #endif
57 
58 #if (defined SUT_ENCODE_ENABLE) || (defined ENABLE_ASTC_ENCODE_BASED_GPU)
CheckClBinIsExist(const std::string & name)59 static bool CheckClBinIsExist(const std::string &name)
60 {
61     return (access(name.c_str(), F_OK) != -1); // -1 means that the file is  not exist
62 }
63 #endif
64 
65 #ifdef SUT_ENCODE_ENABLE
66 static const std::string g_textureSuperEncSo = "/system/lib64/module/hms/graphic/libtextureSuperCompress.z.so";
67 using SuperCompressTexture = bool (*)(uint8_t*, int32_t, uint8_t*, int32_t&, uint32_t);
68 
69 class SutEncSoManager {
70 public:
71     SutEncSoManager();
72     ~SutEncSoManager();
73     bool LoadSutEncSo();
74     SuperCompressTexture sutEncSoEncFunc_;
75 private:
76     bool sutEncSoOpened_;
77     void *textureEncSoHandle_;
78 };
79 
80 static SutEncSoManager g_sutEncSoManager;
81 
SutEncSoManager()82 SutEncSoManager::SutEncSoManager()
83 {
84     sutEncSoOpened_ = false;
85     textureEncSoHandle_ = nullptr;
86     sutEncSoEncFunc_ = nullptr;
87 }
88 
~SutEncSoManager()89 SutEncSoManager::~SutEncSoManager()
90 {
91     bool sutEncHasBeenOpen = sutEncSoOpened_ && (textureEncSoHandle_ != nullptr);
92     if (sutEncHasBeenOpen) {
93         int ret = dlclose(textureEncSoHandle_);
94         IMAGE_LOGD("astcenc dlcose ret: %{public}d %{public}s!", ret, g_textureSuperEncSo.c_str());
95     }
96 }
97 
LoadSutEncSo()98 bool SutEncSoManager::LoadSutEncSo()
99 {
100     if (!sutEncSoOpened_) {
101         if (!CheckClBinIsExist(g_textureSuperEncSo)) {
102             IMAGE_LOGE("sut %{public}s! is not found", g_textureSuperEncSo.c_str());
103             return false;
104         }
105         textureEncSoHandle_ = dlopen(g_textureSuperEncSo.c_str(), 1);
106         if (textureEncSoHandle_ == nullptr) {
107             IMAGE_LOGE("sut libtextureSuperCompress dlopen failed!");
108             return false;
109         }
110         sutEncSoEncFunc_ =
111             reinterpret_cast<SuperCompressTexture>(dlsym(textureEncSoHandle_, "SuperCompressTexture"));
112         if (sutEncSoEncFunc_ == nullptr) {
113             IMAGE_LOGE("sut libtextureSuperCompress dlsym failed!");
114             dlclose(textureEncSoHandle_);
115             textureEncSoHandle_ = nullptr;
116             return false;
117         }
118         IMAGE_LOGD("astcenc dlopen success: %{public}s!", g_textureSuperEncSo.c_str());
119         sutEncSoOpened_ = true;
120     }
121     return true;
122 }
123 #endif
124 
SetAstcEncode(OutputDataStream * outputStream,PlEncodeOptions & option,Media::PixelMap * pixelMap)125 uint32_t AstcCodec::SetAstcEncode(OutputDataStream* outputStream, PlEncodeOptions &option, Media::PixelMap* pixelMap)
126 {
127     if (outputStream == nullptr || pixelMap == nullptr) {
128         IMAGE_LOGE("input data is nullptr.");
129         return ERROR;
130     }
131     astcOutput_ = outputStream;
132     astcOpts_ = option;
133     astcPixelMap_ = pixelMap;
134     return SUCCESS;
135 }
136 
137 // test ASTCEncoder
GenAstcHeader(uint8_t * header,astcenc_image img,TextureEncodeOptions & encodeParams)138 uint32_t GenAstcHeader(uint8_t *header, astcenc_image img, TextureEncodeOptions &encodeParams)
139 {
140     uint8_t *tmp = header;
141     *tmp++ = ASTC_MAGIC_ID & ASTC_MASK;
142     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_8) & ASTC_MASK;
143     *tmp++ = (ASTC_MAGIC_ID >> ASTC_HEADER_SIZE) & ASTC_MASK;
144     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_24) & ASTC_MASK;
145     *tmp++ = static_cast<uint8_t>(encodeParams.blockX_);
146     *tmp++ = static_cast<uint8_t>(encodeParams.blockY_);
147     *tmp++ = 1;
148     *tmp++ = img.dim_x & ASTC_MASK;
149     *tmp++ = (img.dim_x >> ASTC_NUM_8) & ASTC_MASK;
150     *tmp++ = (img.dim_x >> ASTC_HEADER_SIZE) & ASTC_MASK;
151     *tmp++ = img.dim_y & ASTC_MASK;
152     *tmp++ = (img.dim_y >> ASTC_NUM_8) & ASTC_MASK;
153     *tmp++ = (img.dim_y >> ASTC_HEADER_SIZE) & ASTC_MASK;
154     *tmp++ = img.dim_z & ASTC_MASK;
155     *tmp++ = (img.dim_z >> ASTC_NUM_8) & ASTC_MASK;
156     *tmp++ = (img.dim_z >> ASTC_HEADER_SIZE) & ASTC_MASK;
157     return SUCCESS;
158 }
159 
InitAstcencConfig(AstcEncoder * work,TextureEncodeOptions * option)160 uint32_t InitAstcencConfig(AstcEncoder* work, TextureEncodeOptions* option)
161 {
162     bool invalidInput = (work == nullptr) || (option == nullptr);
163     if (invalidInput) {
164         IMAGE_LOGE("astc input work or option is nullptr.");
165         return ERROR;
166     }
167     unsigned int blockX = option->blockX_;
168     unsigned int blockY = option->blockY_;
169     unsigned int blockZ = 1;
170 
171     float quality = ASTCENC_PRE_FAST;
172     unsigned int flags = ASTCENC_FLG_SELF_DECOMPRESS_ONLY;
173     astcenc_error status = astcenc_config_init(work->profile, blockX, blockY,
174         blockZ, quality, flags, &work->config);
175     if (status != ASTCENC_SUCCESS) {
176         IMAGE_LOGE("ERROR: astcenc_config_init failed, status %{public}d", status);
177         return ERROR;
178     }
179     work->config.privateProfile = option->privateProfile_;
180     if (work->config.privateProfile == HIGH_SPEED_PROFILE) {
181         work->config.tune_refinement_limit = 1;
182         work->config.tune_candidate_limit = 1;
183         work->config.tune_partition_count_limit = 1;
184     }
185     if (astcenc_context_alloc(&work->config, 1, &work->codec_context) != ASTCENC_SUCCESS) {
186         return ERROR;
187     }
188     return SUCCESS;
189 }
190 
extractDimensions(std::string & format,TextureEncodeOptions & param)191 void extractDimensions(std::string &format, TextureEncodeOptions &param)
192 {
193     param.blockX_ = DEFAULT_DIM;
194     param.blockY_ = DEFAULT_DIM;
195     std::size_t slashPos = format.rfind('/');
196     if (slashPos != std::string::npos) {
197         std::string dimensions = format.substr(slashPos + 1);
198         std::size_t starPos = dimensions.find('*');
199         if (starPos != std::string::npos) {
200             std::string widthStr = dimensions.substr(0, starPos);
201             std::string heightStr = dimensions.substr(starPos + 1);
202 
203             auto ret_x = std::from_chars(widthStr.data(), widthStr.data() + widthStr.size(), param.blockX_);
204             auto ret_y = std::from_chars(heightStr.data(), heightStr.data() + heightStr.size(), param.blockY_);
205             if (!(ret_x.ec == std::errc() && ret_y.ec == std::errc())) {
206                 IMAGE_LOGE("Failed to convert string to number");
207             }
208         }
209     }
210 }
211 
212 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
213 constexpr double MAX_PSNR = 99.9;
214 constexpr double MAX_VALUE = 255;
215 constexpr double THRESHOLD_R = 30.0;
216 constexpr double THRESHOLD_G = 30.0;
217 constexpr double THRESHOLD_B = 30.0;
218 constexpr double THRESHOLD_A = 30.0;
219 constexpr double THRESHOLD_RGB = 30.0;
220 constexpr double LOG_BASE = 10.0;
CheckQuality(int32_t * mseIn[RGBA_COM],int blockNum,int blockXYZ)221 bool CheckQuality(int32_t *mseIn[RGBA_COM], int blockNum, int blockXYZ)
222 {
223     double psnr[RGBA_COM + 1];
224     const double threshold[RGBA_COM + 1] = {THRESHOLD_R, THRESHOLD_G, THRESHOLD_B, THRESHOLD_A, THRESHOLD_RGB};
225     uint64_t mseTotal[RGBA_COM + 1] = {0, 0, 0, 0, 0};
226     for (int i = R_COM; i < RGBA_COM; i++) {
227         int32_t *mse = mseIn[i];
228         if (!mse) {
229             return false;
230         }
231         for (int j = 0; j < blockNum; j++) {
232             mseTotal[i] += *mse;
233             if (i != A_COM) mseTotal[RGBA_COM] += *mse;
234             mse++;
235         }
236     }
237     for (int i = R_COM; i < RGBA_COM; i++) {
238         if (mseTotal[i] == 0) {
239             psnr[i] = MAX_PSNR;
240             continue;
241         }
242         double mseRgb = static_cast<double>(mseTotal[i] / static_cast<uint64_t>((blockNum * blockXYZ)));
243         psnr[i] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
244     }
245     if (mseTotal[RGBA_COM] == 0) {
246         psnr[RGBA_COM] = MAX_PSNR;
247     } else {
248         double mseRgb = static_cast<double>(
249             mseTotal[RGBA_COM] / static_cast<uint64_t>((blockNum * blockXYZ * (RGBA_COM - 1))));
250         psnr[RGBA_COM] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
251     }
252     IMAGE_LOGD("astc psnr r%{public}f g%{public}f b%{public}f a%{public}f rgb%{public}f",
253         psnr[R_COM], psnr[G_COM], psnr[B_COM], psnr[A_COM],
254         psnr[RGBA_COM]);
255     return (psnr[R_COM] > threshold[R_COM]) && (psnr[G_COM] > threshold[G_COM])
256         && (psnr[B_COM] > threshold[B_COM]) && (psnr[A_COM] > threshold[A_COM])
257         && (psnr[RGBA_COM] > threshold[RGBA_COM]);
258 }
259 #endif
260 
FreeMem(AstcEncoder * work)261 static void FreeMem(AstcEncoder *work)
262 {
263     if (!work) {
264         return;
265     }
266 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
267     if (work->calQualityEnable) {
268         for (int i = R_COM; i < RGBA_COM; i++) {
269             if (work->mse[i]) {
270                 free(work->mse[i]);
271                 work->mse[i] = nullptr;
272             }
273         }
274     }
275 #endif
276     if (work->image_.data) {
277         free(work->image_.data);
278         work->image_.data = nullptr;
279     }
280     if (work->codec_context != nullptr) {
281         astcenc_context_free(work->codec_context);
282         work->codec_context = nullptr;
283     }
284     work->data_out_ = nullptr;
285 }
286 
InitMem(AstcEncoder * work,TextureEncodeOptions param)287 static bool InitMem(AstcEncoder *work, TextureEncodeOptions param)
288 {
289     if (!work) {
290         return false;
291     }
292     work->swizzle_ = {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
293     work->image_.dim_x = static_cast<unsigned int>(param.width_);
294     work->image_.dim_y = static_cast<unsigned int>(param.height_);
295     work->image_.dim_z = 1;
296     work->image_.data_type = ASTCENC_TYPE_U8;
297     work->image_.dim_stride = static_cast<unsigned int>(param.stride_);
298     work->codec_context = nullptr;
299     work->image_.data = nullptr;
300     work->profile = ASTCENC_PRF_LDR_SRGB;
301 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
302     work->mse[R_COM] = work->mse[G_COM] = work->mse[B_COM] = work->mse[RGBA_COM] = nullptr;
303     work->calQualityEnable = param.enableQualityCheck;
304     if (work->calQualityEnable) {
305         for (int i = R_COM; i < RGBA_COM; i++) {
306             work->mse[i] = static_cast<int32_t *>(calloc(param.blocksNum, sizeof(int32_t)));
307             if (!work->mse[i]) {
308                 IMAGE_LOGE("quality control calloc failed");
309                 return false;
310             }
311         }
312     }
313 #endif
314     work->image_.data = static_cast<void **>(malloc(sizeof(void*) * work->image_.dim_z));
315     if (!work->image_.data) {
316         return false;
317     }
318     return true;
319 }
320 
AstcSoftwareEncodeCore(TextureEncodeOptions & param,uint8_t * pixmapIn,uint8_t * astcBuffer)321 bool AstcCodec::AstcSoftwareEncodeCore(TextureEncodeOptions &param, uint8_t *pixmapIn, uint8_t *astcBuffer)
322 {
323     if ((pixmapIn == nullptr) || (astcBuffer == nullptr)) {
324         IMAGE_LOGE("pixmapIn or astcBuffer is nullptr");
325         return false;
326     }
327     AstcEncoder work;
328     if (!InitMem(&work, param)) {
329         FreeMem(&work);
330         return false;
331     }
332     if (InitAstcencConfig(&work, &param) != SUCCESS) {
333         IMAGE_LOGE("astc InitAstcencConfig failed");
334         FreeMem(&work);
335         return false;
336     }
337     work.image_.data[0] = pixmapIn;
338     work.data_out_ = astcBuffer;
339     if (GenAstcHeader(work.data_out_, work.image_, param) != SUCCESS) {
340         IMAGE_LOGE("astc GenAstcHeader failed");
341         FreeMem(&work);
342         return false;
343     }
344     work.error_ = astcenc_compress_image(work.codec_context, &work.image_, &work.swizzle_,
345         work.data_out_ + TEXTURE_HEAD_BYTES, param.astcBytes - TEXTURE_HEAD_BYTES,
346 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
347         work.calQualityEnable, work.mse,
348 #endif
349         0);
350 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
351     if ((ASTCENC_SUCCESS != work.error_) ||
352         (work.calQualityEnable && !CheckQuality(work.mse, param.blocksNum, param.blockX_ * param.blockY_))) {
353 #else
354     if (ASTCENC_SUCCESS != work.error_) {
355 #endif
356         IMAGE_LOGE("astc compress failed");
357         FreeMem(&work);
358         return false;
359     }
360     FreeMem(&work);
361     return true;
362 }
363 
364 static QualityProfile GetAstcQuality(int32_t quality)
365 {
366     QualityProfile privateProfile;
367     switch (quality) {
368         case HIGH_SPEED_PROFILE_MAP_QUALITY:
369             privateProfile = HIGH_SPEED_PROFILE;
370             break;
371         default:
372             privateProfile = HIGH_QUALITY_PROFILE;
373             break;
374     }
375     return privateProfile;
376 }
377 
378 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
379 bool AstcCodec::TryAstcEncBasedOnCl(TextureEncodeOptions &param, uint8_t *inData,
380     uint8_t *buffer, const std::string &clBinPath)
381 {
382     ClAstcHandle *astcClEncoder = nullptr;
383     bool invalidPara = (inData == nullptr) || (buffer == nullptr);
384     if (invalidPara) {
385         IMAGE_LOGE("astc Please check TryAstcEncBasedOnCl input!");
386         return false;
387     }
388     if (AstcClCreate(&astcClEncoder, clBinPath) != CL_ASTC_ENC_SUCCESS) {
389         IMAGE_LOGE("astc AstcClCreate failed!");
390         return false;
391     }
392     ClAstcImageOption imageIn;
393     if (AstcClFillImage(&imageIn, inData, param.stride_, param.width_, param.height_) != CL_ASTC_ENC_SUCCESS) {
394         IMAGE_LOGE("astc AstcClFillImage failed!");
395         AstcClClose(astcClEncoder);
396         return false;
397     }
398     if (AstcClEncImage(astcClEncoder, &imageIn, buffer) != CL_ASTC_ENC_SUCCESS) {
399         IMAGE_LOGE("astc AstcClEncImage failed!");
400         AstcClClose(astcClEncoder);
401         return false;
402     }
403     if (AstcClClose(astcClEncoder) != CL_ASTC_ENC_SUCCESS) {
404         IMAGE_LOGE("astc AstcClClose failed!");
405         return false;
406     }
407     return true;
408 }
409 #endif
410 
411 #ifdef SUT_ENCODE_ENABLE
412 bool AstcCodec::TryTextureSuperCompress(TextureEncodeOptions &param, uint8_t *astcBuffer)
413 {
414     bool skipSutEnc = (param.sutProfile == SutProfile::SKIP_SUT) ||
415         ((!param.hardwareFlag) && (param.privateProfile_ != HIGH_SPEED_PROFILE)) ||
416         (param.blockX_ != DEFAULT_DIM && param.blockY_ != DEFAULT_DIM);
417     if (skipSutEnc) {
418         IMAGE_LOGD("astc is not suit to be compressed to sut!");
419         param.sutProfile = SutProfile::SKIP_SUT;
420         return true;
421     }
422     param.sutBytes = param.astcBytes;
423     uint8_t *sutBuffer = static_cast<uint8_t *>(malloc(param.sutBytes));
424     if (sutBuffer == nullptr) {
425         IMAGE_LOGE("astc sutBuffer malloc failed!");
426         return false;
427     }
428     bool invalidSutEnc = !g_sutEncSoManager.LoadSutEncSo() || g_sutEncSoManager.sutEncSoEncFunc_ == nullptr;
429     if (invalidSutEnc) {
430         IMAGE_LOGE("sut enc so dlopen failed or sutEncSoEncFunc_ is nullptr!");
431         free(sutBuffer);
432         return false;
433     }
434     if (!g_sutEncSoManager.sutEncSoEncFunc_(astcBuffer,
435         param.astcBytes, sutBuffer, param.sutBytes, static_cast<uint32_t>(param.sutProfile))) {
436         IMAGE_LOGE("astc g_sutEncSoEncFunc failed!");
437         free(sutBuffer);
438         return false;
439     }
440     if (memcpy_s(astcBuffer, param.astcBytes, sutBuffer, param.sutBytes) < 0) {
441         IMAGE_LOGE("sut sutbuffer is failed to be copied to astcBuffer!");
442         free(sutBuffer);
443         return false;
444     }
445     free(sutBuffer);
446     param.astcBytes = param.sutBytes;
447     param.outIsSut = true;
448     return true;
449 }
450 #endif
451 
452 static bool InitAstcEncPara(TextureEncodeOptions &param,
453     int32_t width, int32_t height, int32_t stride, PlEncodeOptions &astcOpts)
454 {
455     param.enableQualityCheck = false;
456     param.hardwareFlag = false;
457     param.sutProfile =
458         ImageSystemProperties::GetSutEncodeEnabled() ? SutProfile::EXTREME_SPEED : SutProfile::SKIP_SUT;
459     param.width_ = width;
460     param.height_ = height;
461     param.stride_ = stride;
462     param.privateProfile_ = GetAstcQuality(astcOpts.quality);
463     param.outIsSut = false;
464     extractDimensions(astcOpts.format, param);
465     if ((param.blockX_ < DEFAULT_DIM) || (param.blockY_ < DEFAULT_DIM)) { // DEFAULT_DIM = 4
466         IMAGE_LOGE("InitAstcEncPara failed %{public}dx%{public}d is invalid!", param.blockX_, param.blockY_);
467         return false;
468     }
469     param.blocksNum = ((param.width_ + param.blockX_ - 1) / param.blockX_) *
470         ((param.height_ + param.blockY_ - 1) / param.blockY_);
471     param.astcBytes = param.blocksNum * TEXTURE_HEAD_BYTES + TEXTURE_HEAD_BYTES;
472     return true;
473 }
474 
475 uint32_t AstcCodec::AstcSoftwareEncode(TextureEncodeOptions &param, bool enableQualityCheck,
476     int32_t blocksNum, uint8_t *outBuffer, int32_t outSize)
477 {
478     ImageInfo imageInfo;
479     astcPixelMap_->GetImageInfo(imageInfo);
480     uint8_t *pixmapIn = static_cast<uint8_t *>(astcPixelMap_->GetWritablePixels());
481     uint32_t stride = static_cast<uint32_t>(astcPixelMap_->GetRowStride()) >> RGBA_BYTES_PIXEL_LOG2;
482     if (!InitAstcEncPara(param, imageInfo.size.width, imageInfo.size.height, static_cast<int32_t>(stride), astcOpts_)) {
483         IMAGE_LOGE("InitAstcEncPara failed");
484         return ERROR;
485     }
486     param.enableQualityCheck = enableQualityCheck;
487     if (!AstcSoftwareEncodeCore(param, pixmapIn, outBuffer)) {
488         IMAGE_LOGE("AstcSoftwareEncodeCore failed");
489         return ERROR;
490     }
491     return SUCCESS;
492 }
493 
494 static bool AstcEncProcess(TextureEncodeOptions &param, uint8_t *pixmapIn, uint8_t *astcBuffer)
495 {
496 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
497     bool openClEnc = param.width_ >= WIDTH_CL_THRESHOLD && param.height_ >= HEIGHT_CL_THRESHOLD &&
498         param.privateProfile_ == QualityProfile::HIGH_SPEED_PROFILE;
499     bool enableClEnc = openClEnc && ImageSystemProperties::GetAstcHardWareEncodeEnabled() &&
500         (param.blockX_ == DEFAULT_DIM) && (param.blockY_ == DEFAULT_DIM); // HardWare only support 4x4 now
501     if (enableClEnc) {
502         IMAGE_LOGI("astc hardware encode begin");
503         std::string clBinPath = "/sys_prod/etc/graphic/AstcEncShader_ALN-AL00.bin";
504         param.hardwareFlag = CheckClBinIsExist(clBinPath) &&
505             AstcCodec::TryAstcEncBasedOnCl(param, pixmapIn, astcBuffer, clBinPath);
506     }
507 #endif
508     if (!param.hardwareFlag) {
509         if (!AstcCodec::AstcSoftwareEncodeCore(param, pixmapIn, astcBuffer)) {
510             IMAGE_LOGE("AstcSoftwareEncodeCore failed");
511             return false;
512         }
513         IMAGE_LOGD("astc software encode success!");
514     }
515     return true;
516 }
517 
518 uint32_t AstcCodec::ASTCEncode()
519 {
520     ImageInfo imageInfo;
521     astcPixelMap_->GetImageInfo(imageInfo);
522     TextureEncodeOptions param;
523     uint8_t *pixmapIn = static_cast<uint8_t *>(astcPixelMap_->GetWritablePixels());
524     int32_t stride = astcPixelMap_->GetRowStride() >> RGBA_BYTES_PIXEL_LOG2;
525     if (!InitAstcEncPara(param, imageInfo.size.width, imageInfo.size.height, stride, astcOpts_)) {
526         IMAGE_LOGE("InitAstcEncPara failed");
527         return ERROR;
528     }
529     AstcExtendInfo extendInfo = {0};
530     if (!InitAstcExtendInfo(extendInfo)) {
531         IMAGE_LOGE("InitAstcExtendInfo failed");
532         return ERROR;
533     }
534     uint32_t packSize = static_cast<uint32_t>(param.astcBytes) +
535         extendInfo.extendBufferSumBytes + ASTC_EXTEND_INFO_SIZE_DEFINITION_LENGTH;
536     uint8_t *astcBuffer = static_cast<uint8_t *>(malloc(packSize));
537     if (astcBuffer == nullptr) {
538         IMAGE_LOGE("astc astcBuffer malloc failed!");
539         ReleaseExtendInfoMemory(extendInfo);
540         return ERROR;
541     }
542     if (!AstcEncProcess(param, pixmapIn, astcBuffer)) {
543         IMAGE_LOGE("astc AstcEncProcess failed!");
544         ReleaseExtendInfoMemory(extendInfo);
545         free(astcBuffer);
546         return ERROR;
547     }
548 #ifdef SUT_ENCODE_ENABLE
549     if (!TryTextureSuperCompress(param, astcBuffer)) {
550         IMAGE_LOGE("astc TryTextureSuperCompress failed!");
551         ReleaseExtendInfoMemory(extendInfo);
552         free(astcBuffer);
553         return ERROR;
554     }
555 #endif
556     if (!param.outIsSut) { // only support astc for color space
557         WriteAstcExtendInfo(astcBuffer, static_cast<uint32_t>(param.astcBytes), extendInfo);
558     } else {
559         packSize = static_cast<uint32_t>(param.sutBytes);
560     }
561     ReleaseExtendInfoMemory(extendInfo);
562     astcOutput_->Write(astcBuffer, packSize);
563     free(astcBuffer);
564     IMAGE_LOGD("astcenc end: %{public}dx%{public}d, GpuFlag %{public}d, sut%{public}d",
565         imageInfo.size.width, imageInfo.size.height, param.hardwareFlag, param.sutProfile);
566     astcOutput_->SetOffset(packSize);
567     return SUCCESS;
568 }
569 
570 bool AllocMemForExtInfo(AstcExtendInfo &extendInfo, uint8_t idx)
571 {
572     AstcExtendInfoType type = static_cast<AstcExtendInfoType>(idx);
573     switch (type) {
574         case AstcExtendInfoType::COLOR_SPACE:
575             extendInfo.extendInfoLength[idx] = ASTC_EXTEND_INFO_COLOR_SPACE_VALUE_LENGTH;
576             extendInfo.extendBufferSumBytes += ASTC_EXTEND_INFO_TYPE_LENGTH +
577                 ASTC_EXTEND_INFO_LENGTH_LENGTH + ASTC_EXTEND_INFO_COLOR_SPACE_VALUE_LENGTH;
578             extendInfo.extendInfoValue[idx] = static_cast<uint8_t*>(malloc(extendInfo.extendInfoLength[idx]));
579             if (extendInfo.extendInfoValue[idx] == nullptr) {
580                 IMAGE_LOGE("[AstcCodec] SetColorSpaceInfo malloc failed!");
581                 return false;
582             }
583             break;
584         default:
585             return false;
586     }
587     return true;
588 }
589 
590 bool AstcCodec::InitAstcExtendInfo(AstcExtendInfo &extendInfo)
591 {
592     if (memset_s(&extendInfo, sizeof(AstcExtendInfo), 0, sizeof(AstcExtendInfo)) != 0) {
593         return false;
594     }
595     extendInfo.extendNums = ASTC_EXTEND_INFO_TLV_NUM;
596     extendInfo.extendBufferSumBytes = 0;
597     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
598         if (!AllocMemForExtInfo(extendInfo, idx)) {
599             ReleaseExtendInfoMemory(extendInfo);
600             IMAGE_LOGE("[AstcCodec] AllocMemForExtInfo failed!");
601             return false;
602         }
603     }
604     return true;
605 }
606 
607 void AstcCodec::ReleaseExtendInfoMemory(AstcExtendInfo &extendInfo)
608 {
609     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
610         if (extendInfo.extendInfoValue[idx] != nullptr) {
611             free(extendInfo.extendInfoValue[idx]);
612             extendInfo.extendInfoValue[idx] = nullptr;
613         }
614     }
615 }
616 
617 static void FillDataSize(uint8_t *buf, uint32_t bytes)
618 {
619     *buf++ = (bytes) & MASKBITS_FOR_8BITS;
620     *buf++ = (bytes >> UINT32_1TH_BYTES) & MASKBITS_FOR_8BITS;
621     *buf++ = (bytes >> UINT32_2TH_BYTES) & MASKBITS_FOR_8BITS;
622     *buf++ = (bytes >> UINT32_3TH_BYTES) & MASKBITS_FOR_8BITS;
623 }
624 
625 void AstcCodec::WriteAstcExtendInfo(uint8_t *buffer, uint32_t offset, AstcExtendInfo &extendInfo)
626 {
627     uint8_t* offsetBuffer = buffer + offset;
628     FillDataSize(offsetBuffer, extendInfo.extendBufferSumBytes);
629     offsetBuffer += ASTC_EXTEND_INFO_SIZE_DEFINITION_LENGTH;
630 #ifdef IMAGE_COLORSPACE_FLAG
631     ColorManager::ColorSpace colorspace = astcPixelMap_->InnerGetGrColorSpace();
632     ColorManager::ColorSpaceName csName = colorspace.GetColorSpaceName();
633 #endif
634     for (uint8_t idx = 0; idx < extendInfo.extendNums; idx++) {
635         *offsetBuffer++ = idx;
636         FillDataSize(offsetBuffer, extendInfo.extendInfoLength[idx]);
637         offsetBuffer += ASTC_EXTEND_INFO_LENGTH_LENGTH;
638         AstcExtendInfoType type = static_cast<AstcExtendInfoType>(idx);
639         switch (type) {
640             case AstcExtendInfoType::COLOR_SPACE:
641 #ifdef IMAGE_COLORSPACE_FLAG
642                 *offsetBuffer = static_cast<uint8_t>(csName);
643 #else
644                 *offsetBuffer = 0;
645 #endif
646                 break;
647             default:
648                 return;
649         }
650     }
651 }
652 } // namespace ImagePlugin
653 } // namespace OHOS
654