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 ¶m)
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 ¶m, 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, ¶m) != 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 ¶m, 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 ¶m,
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 ¶m, 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 ¶m, 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 ¶m)
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 ¶m,
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 ¶m, 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 ¶m, 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 ¶m, 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 ¶m, 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 ¶m,
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 ¶m, 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 ¶m, 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