• 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 "astc_codec.h"
17 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
18 #include "image_compressor.h"
19 #endif
20 #include "image_log.h"
21 #include "image_system_properties.h"
22 #include "securec.h"
23 #include "media_errors.h"
24 
25 #undef LOG_DOMAIN
26 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_PLUGIN
27 
28 #undef LOG_TAG
29 #define LOG_TAG "AstcCodec"
30 
31 namespace OHOS {
32 namespace ImagePlugin {
33 using namespace Media;
34 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
35 using namespace AstcEncBasedCl;
36 #endif
37 constexpr uint8_t TEXTURE_HEAD_BYTES = 16;
38 constexpr uint8_t ASTC_MASK = 0xFF;
39 constexpr uint8_t ASTC_NUM_8 = 8;
40 constexpr uint8_t ASTC_HEADER_SIZE = 16;
41 constexpr uint8_t ASTC_NUM_24 = 24;
42 static const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
43 constexpr uint8_t DEFAULT_DIM = 4;
44 constexpr uint8_t HIGH_SPEED_PROFILE_MAP_QUALITY = 20; // quality level is 20 for thumbnail
45 
SetAstcEncode(OutputDataStream * outputStream,PlEncodeOptions & option,Media::PixelMap * pixelMap)46 uint32_t AstcCodec::SetAstcEncode(OutputDataStream* outputStream, PlEncodeOptions &option, Media::PixelMap* pixelMap)
47 {
48     if (outputStream == nullptr || pixelMap == nullptr) {
49         IMAGE_LOGE("input data is nullptr.");
50         return ERROR;
51     }
52     astcOutput_ = outputStream;
53     astcOpts_ = option;
54     astcPixelMap_ = pixelMap;
55     return SUCCESS;
56 }
57 
58 // test ASTCEncoder
GenAstcHeader(uint8_t * header,astcenc_image img,TextureEncodeOptions * encodeParams,size_t size)59 uint32_t GenAstcHeader(uint8_t *header, astcenc_image img, TextureEncodeOptions *encodeParams, size_t size)
60 {
61     if ((encodeParams == nullptr) || (header == nullptr) || size < ASTC_HEADER_SIZE) {
62         IMAGE_LOGE("header is nullptr or encodeParams is nullptr or header_size is error");
63         return ERROR;
64     }
65     uint8_t *tmp = header;
66     *tmp++ = ASTC_MAGIC_ID & ASTC_MASK;
67     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_8) & ASTC_MASK;
68     *tmp++ = (ASTC_MAGIC_ID >> ASTC_HEADER_SIZE) & ASTC_MASK;
69     *tmp++ = (ASTC_MAGIC_ID >> ASTC_NUM_24) & ASTC_MASK;
70     *tmp++ = static_cast<uint8_t>(encodeParams->blockX_);
71     *tmp++ = static_cast<uint8_t>(encodeParams->blockY_);
72     *tmp++ = 1;
73     *tmp++ = img.dim_x & ASTC_MASK;
74     *tmp++ = (img.dim_x >> ASTC_NUM_8) & ASTC_MASK;
75     *tmp++ = (img.dim_x >> ASTC_HEADER_SIZE) & ASTC_MASK;
76     *tmp++ = img.dim_y & ASTC_MASK;
77     *tmp++ = (img.dim_y >> ASTC_NUM_8) & ASTC_MASK;
78     *tmp++ = (img.dim_y >> ASTC_HEADER_SIZE) & ASTC_MASK;
79     *tmp++ = img.dim_z & ASTC_MASK;
80     *tmp++ = (img.dim_z >> ASTC_NUM_8) & ASTC_MASK;
81     *tmp++ = (img.dim_z >> ASTC_HEADER_SIZE) & ASTC_MASK;
82     return SUCCESS;
83 }
84 
InitAstcencConfig(AstcEncoder * work,TextureEncodeOptions * option)85 uint32_t InitAstcencConfig(AstcEncoder* work, TextureEncodeOptions* option)
86 {
87     if ((work == nullptr) || (option == nullptr)) {
88         IMAGE_LOGE("astc input work or option is nullptr.");
89         return ERROR;
90     }
91     unsigned int blockX = option->blockX_;
92     unsigned int blockY = option->blockY_;
93     unsigned int blockZ = 1;
94 
95     float quality = ASTCENC_PRE_FAST;
96     unsigned int flags = ASTCENC_FLG_SELF_DECOMPRESS_ONLY;
97     astcenc_error status = astcenc_config_init(work->profile, blockX, blockY,
98         blockZ, quality, flags, &work->config);
99     if (status == ASTCENC_ERR_BAD_BLOCK_SIZE) {
100         IMAGE_LOGE("ERROR: block size is invalid");
101         return ERROR;
102     } else if (status == ASTCENC_ERR_BAD_CPU_FLOAT) {
103         IMAGE_LOGE("ERROR: astcenc must not be compiled with fast-math");
104         return ERROR;
105     } else if (status != ASTCENC_SUCCESS) {
106         IMAGE_LOGE("ERROR: config failed");
107         return ERROR;
108     }
109     work->config.privateProfile = option->privateProfile_;
110     if (work->config.privateProfile == HIGH_SPEED_PROFILE) {
111         work->config.tune_refinement_limit = 1;
112         work->config.tune_candidate_limit = 1;
113         work->config.tune_partition_count_limit = 1;
114     }
115     if (astcenc_context_alloc(&work->config, 1, &work->codec_context) != ASTCENC_SUCCESS) {
116         return ERROR;
117     }
118     return SUCCESS;
119 }
120 
extractDimensions(std::string & format,TextureEncodeOptions & param)121 void extractDimensions(std::string &format, TextureEncodeOptions &param)
122 {
123     param.blockX_ = DEFAULT_DIM;
124     param.blockY_ = DEFAULT_DIM;
125     std::size_t slashPos = format.rfind('/');
126     if (slashPos != std::string::npos) {
127         std::string dimensions = format.substr(slashPos + 1);
128         std::size_t starPos = dimensions.find('*');
129         if (starPos != std::string::npos) {
130             std::string widthStr = dimensions.substr(0, starPos);
131             std::string heightStr = dimensions.substr(starPos + 1);
132 
133             param.blockX_ = static_cast<uint8_t>(std::stoi(widthStr));
134             param.blockY_ = static_cast<uint8_t>(std::stoi(heightStr));
135         }
136     }
137 }
138 
139 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
140 constexpr double MAX_PSNR = 99.9;
141 constexpr double MAX_VALUE = 255;
142 constexpr double THRESHOLD_R = 30.0;
143 constexpr double THRESHOLD_G = 30.0;
144 constexpr double THRESHOLD_B = 30.0;
145 constexpr double THRESHOLD_A = 30.0;
146 constexpr double THRESHOLD_RGB = 30.0;
147 constexpr double LOG_BASE = 10.0;
CheckQuality(int32_t * mseIn[RGBA_COM],int blockNum,int blockXYZ)148 bool CheckQuality(int32_t *mseIn[RGBA_COM], int blockNum, int blockXYZ)
149 {
150     double psnr[RGBA_COM + 1];
151     const double threshold[RGBA_COM + 1] = {THRESHOLD_R, THRESHOLD_G, THRESHOLD_B, THRESHOLD_A, THRESHOLD_RGB};
152     uint64_t mseTotal[RGBA_COM + 1] = {0, 0, 0, 0, 0};
153     for (int i = R_COM; i < RGBA_COM; i++) {
154         int32_t *mse = mseIn[i];
155         if (!mse) {
156             return false;
157         }
158         for (int j = 0; j < blockNum; j++) {
159             mseTotal[i] += *mse;
160             if (i != A_COM) mseTotal[RGBA_COM] += *mse;
161             mse++;
162         }
163     }
164     for (int i = R_COM; i < RGBA_COM; i++) {
165         if (mseTotal[i] == 0) {
166             psnr[i] = MAX_PSNR;
167             continue;
168         }
169         double mseRgb = static_cast<double>(mseTotal[i] / (blockNum * blockXYZ));
170         psnr[i] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
171     }
172     if (mseTotal[RGBA_COM] == 0) {
173         psnr[RGBA_COM] = MAX_PSNR;
174     } else {
175         double mseRgb = static_cast<double>(mseTotal[RGBA_COM] / (blockNum * blockXYZ * (RGBA_COM - 1)));
176         psnr[RGBA_COM] = LOG_BASE * log(static_cast<double>(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
177     }
178     IMAGE_LOGD("astc psnr r%{public}f g%{public}f b%{public}f a%{public}f rgb%{public}f",
179         psnr[R_COM], psnr[G_COM], psnr[B_COM], psnr[A_COM],
180         psnr[RGBA_COM]);
181     return (psnr[R_COM] > threshold[R_COM]) && (psnr[G_COM] > threshold[G_COM])
182         && (psnr[B_COM] > threshold[B_COM]) && (psnr[A_COM] > threshold[A_COM])
183         && (psnr[RGBA_COM] > threshold[RGBA_COM]);
184 }
185 #endif
186 
FreeMem(AstcEncoder * work)187 static void FreeMem(AstcEncoder *work)
188 {
189     if (!work) {
190         return;
191     }
192 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
193     if (work->calQualityEnable) {
194         for (int i = R_COM; i < RGBA_COM; i++) {
195             if (work->mse[i]) {
196                 free(work->mse[i]);
197                 work->mse[i] = nullptr;
198             }
199         }
200     }
201 #endif
202     if (work->image_.data) {
203         free(work->image_.data);
204         work->image_.data = nullptr;
205     }
206     if (work->codec_context != nullptr) {
207         astcenc_context_free(work->codec_context);
208         work->codec_context = nullptr;
209     }
210 }
211 
InitMem(AstcEncoder * work,TextureEncodeOptions param,bool enableQualityCheck,int blockNum)212 static bool InitMem(AstcEncoder *work, TextureEncodeOptions param, bool enableQualityCheck, int blockNum)
213 {
214     if (!work) {
215         return false;
216     }
217     work->swizzle_ = {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
218     work->image_.dim_x = param.width_;
219     work->image_.dim_y = param.height_;
220     work->image_.dim_z = 1;
221     work->image_.data_type = ASTCENC_TYPE_U8;
222     work->image_.dim_stride = param.stride_;
223     work->codec_context = nullptr;
224     work->image_.data = nullptr;
225     work->profile = ASTCENC_PRF_LDR_SRGB;
226 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
227     work->mse[R_COM] = work->mse[G_COM] = work->mse[B_COM] = work->mse[RGBA_COM] = nullptr;
228     work->calQualityEnable = enableQualityCheck;
229     if (work->calQualityEnable) {
230         for (int i = R_COM; i < RGBA_COM; i++) {
231             work->mse[i] = static_cast<int32_t *>(calloc(blockNum, sizeof(int32_t)));
232             if (!work->mse[i]) {
233                 IMAGE_LOGE("quality control calloc failed");
234                 return false;
235             }
236         }
237     }
238 #endif
239     work->image_.data = static_cast<void **>(malloc(sizeof(void*) * work->image_.dim_z));
240     if (!work->image_.data) {
241         return false;
242     }
243     return true;
244 }
245 
246 constexpr uint8_t RGBA_BYTES_PIXEL_LOG2 = 2;
247 
AstcSoftwareEncode(TextureEncodeOptions & param,bool enableQualityCheck,int32_t blocksNum,int32_t outSize)248 uint32_t AstcCodec::AstcSoftwareEncode(TextureEncodeOptions &param, bool enableQualityCheck,
249                                        int32_t blocksNum, int32_t outSize)
250 {
251     AstcEncoder work;
252     if (!InitMem(&work, param, enableQualityCheck, blocksNum)) {
253         FreeMem(&work);
254         return ERROR;
255     }
256     if (InitAstcencConfig(&work, &param) != SUCCESS) {
257         IMAGE_LOGE("astc InitAstcencConfig failed");
258         FreeMem(&work);
259         return ERROR;
260     }
261     work.image_.data[0] = static_cast<uint8_t *>(astcPixelMap_->GetWritablePixels());
262     work.data_out_ = astcOutput_->GetAddr();
263     size_t size;
264     astcOutput_->GetCapicity(size);
265     if (GenAstcHeader(work.data_out_, work.image_, &param, size) != SUCCESS) {
266         IMAGE_LOGE("astc GenAstcHeader failed");
267         FreeMem(&work);
268         return ERROR;
269     }
270     work.error_ = astcenc_compress_image(work.codec_context, &work.image_, &work.swizzle_,
271         work.data_out_ + TEXTURE_HEAD_BYTES, outSize - TEXTURE_HEAD_BYTES,
272 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
273         work.calQualityEnable, work.mse,
274 #endif
275         0);
276 #if defined(QUALITY_CONTROL) && (QUALITY_CONTROL == 1)
277     if ((ASTCENC_SUCCESS != work.error_) ||
278         (work.calQualityEnable && !CheckQuality(work.mse, blocksNum, param.blockX_ * param.blockY_))) {
279 #else
280     if (ASTCENC_SUCCESS != work.error_) {
281 #endif
282         IMAGE_LOGE("astc compress failed");
283         FreeMem(&work);
284         return ERROR;
285     }
286     FreeMem(&work);
287     return SUCCESS;
288 }
289 
290 static QualityProfile GetAstcQuality(int32_t quality)
291 {
292     QualityProfile privateProfile;
293     switch (quality) {
294         case HIGH_SPEED_PROFILE_MAP_QUALITY:
295             privateProfile = HIGH_SPEED_PROFILE;
296             break;
297         default:
298             privateProfile = HIGH_QUALITY_PROFILE;
299             break;
300     }
301     return privateProfile;
302 }
303 
304 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
305 static bool TryAstcEncBasedOnCl(uint8_t *inData, int32_t stride, TextureEncodeOptions *param,
306     uint8_t *buffer, const std::string &clBinPath)
307 {
308     ClAstcHandle *astcClEncoder = nullptr;
309     if ((inData == nullptr) || (param == nullptr) || (buffer == nullptr)) {
310         IMAGE_LOGE("astc Please check TryAstcEncBasedOnCl input!");
311         return false;
312     }
313     if (AstcClCreate(&astcClEncoder, clBinPath) != CL_ASTC_ENC_SUCCESS) {
314         IMAGE_LOGE("astc AstcClCreate failed!");
315         return false;
316     }
317     ClAstcImageOption imageIn;
318     if (AstcClFillImage(&imageIn, inData, stride, param->width_, param->height_) != CL_ASTC_ENC_SUCCESS) {
319         IMAGE_LOGE("astc AstcClFillImage failed!");
320         AstcClClose(astcClEncoder);
321         return false;
322     }
323     if (AstcClEncImage(astcClEncoder, &imageIn, buffer) != CL_ASTC_ENC_SUCCESS) {
324         IMAGE_LOGE("astc AstcClEncImage failed!");
325         AstcClClose(astcClEncoder);
326         return false;
327     }
328     if (AstcClClose(astcClEncoder) != CL_ASTC_ENC_SUCCESS) {
329         IMAGE_LOGE("astc AstcClClose failed!");
330         return false;
331     }
332     return true;
333 }
334 #endif
335 
336 uint32_t AstcCodec::ASTCEncode()
337 {
338     ImageInfo imageInfo;
339     astcPixelMap_->GetImageInfo(imageInfo);
340     TextureEncodeOptions param;
341     param.width_ = imageInfo.size.width;
342     param.height_ = imageInfo.size.height;
343     param.stride_ = astcPixelMap_->GetRowStride() >> RGBA_BYTES_PIXEL_LOG2;
344     param.privateProfile_ = GetAstcQuality(astcOpts_.quality);
345     bool enableQualityCheck = false; // astcOpts_.enableQualityCheck
346     bool hardwareFlag = false;
347     extractDimensions(astcOpts_.format, param);
348     int32_t blocksNum = ((param.width_ + param.blockX_ - 1) / param.blockX_) *
349         ((param.height_ + param.blockY_ - 1) / param.blockY_);
350     int32_t outSize = blocksNum * TEXTURE_HEAD_BYTES + TEXTURE_HEAD_BYTES;
351 
352 #ifdef ENABLE_ASTC_ENCODE_BASED_GPU
353     if (ImageSystemProperties::GetAstcHardWareEncodeEnabled() &&
354         (param.blockX_ == DEFAULT_DIM) && (param.blockY_ == DEFAULT_DIM)) { // HardWare only support 4x4 now
355         IMAGE_LOGI("astc hardware encode begin");
356         std::string clBinPath = "/data/local/tmp/astcKernelBin.bin";
357         if (TryAstcEncBasedOnCl(static_cast<uint8_t *>(astcPixelMap_->GetWritablePixels()),
358             astcPixelMap_->GetRowStride(), &param, astcOutput_->GetAddr(), clBinPath)) {
359             hardwareFlag = true;
360             IMAGE_LOGI("astc hardware encode success!");
361         } else {
362             IMAGE_LOGI("astc hardware encode failed!");
363         }
364     }
365 #endif
366     if (!hardwareFlag) {
367         uint32_t res = AstcSoftwareEncode(param, enableQualityCheck, blocksNum, outSize);
368         if (res != SUCCESS) {
369             IMAGE_LOGE("AstcSoftwareEncode failed");
370             return ERROR;
371         }
372         IMAGE_LOGD("astc software encode success!");
373     }
374     IMAGE_LOGD("astc hardwareFlag %{public}d, enableQualityCheck %{public}d, privateProfile %{public}d",
375         hardwareFlag, enableQualityCheck, param.privateProfile_);
376     astcOutput_->SetOffset(outSize);
377     return SUCCESS;
378 }
379 } // namespace ImagePlugin
380 } // namespace OHOS