* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cstdio>
#include <cmath>
#include <unistd.h>
#include "image_compress.h"
#include "app_log_wrapper.h"
#include "securec.h"
#include "image_source.h"
#include "image_packer.h"
#include "media_errors.h"
namespace OHOS {
namespace AppExecFwk {
namespace {
constexpr size_t FORMAT_LENGTH = 8;
const std::string JPEG_FORMAT = "image/jpeg";
const std::string PNG_FORMAT = "image/png";
const std::string WEBP_FORMAT = "image/webp";
const std::string BUNDLE_PATH = "/data/app/el1/bundle";
constexpr int32_t QUALITY = 20;
constexpr int32_t MUNBER_ONE = 1;
constexpr int64_t BUFFER_SIZE = 2 * 1024 * 1024;
constexpr int32_t FILE_MAX_SIZE = 10240;
constexpr int32_t FILE_COMPRESS_SIZE = 4096;
constexpr int32_t WEBP_COMPRESS_SIZE = 128;
constexpr uint8_t JPEG_DATA_ZERO = 0xFF;
constexpr uint8_t JPEG_DATA_ONE = 0xD8;
constexpr uint8_t JPEG_DATA_TWO = 0xFF;
constexpr uint8_t PNG_DATA_ZERO = 0x89;
constexpr uint8_t PNG_DATA_ONE = 0x50;
constexpr uint8_t PNG_DATA_TWO = 0x4E;
constexpr uint8_t PNG_DATA_THREE = 0x47;
constexpr int32_t INDEX_ZERO = 0;
constexpr int32_t INDEX_ONE = 1;
constexpr int32_t INDEX_TWO = 2;
constexpr int32_t INDEX_THREE = 3;
constexpr int32_t EMPTY_FILE_SIZE = 0;
constexpr double FILE_SIZE_ERR = -1.0;
}
bool ImageCompress::IsPathValid(const std::string &srcPath)
{
if (srcPath.find(BUNDLE_PATH) != std::string::npos) {
return access(srcPath.c_str(), R_OK) == 0;
} else {
return false;
}
}
bool ImageCompress::IsImageNeedCompressBySize(size_t fileSize)
{
return fileSize > FILE_MAX_SIZE;
}
double ImageCompress::CalculateRatio(size_t fileSize, const std::string &imageType)
{
if (fileSize == EMPTY_FILE_SIZE) {
return FILE_SIZE_ERR;
}
if (imageType == WEBP_FORMAT) {
return sqrt(static_cast<double>(WEBP_COMPRESS_SIZE) / fileSize);
}
return sqrt(static_cast<double>(FILE_COMPRESS_SIZE) / fileSize);
}
ImageType ImageCompress::GetImageType(const std::unique_ptr<uint8_t[]> &fileData, size_t fileLength)
{
if (fileLength < FORMAT_LENGTH) {
return ImageType::WORNG_TYPE;
}
const uint8_t* data = fileData.get();
if (data[INDEX_ZERO] == JPEG_DATA_ZERO && data[INDEX_ONE] == JPEG_DATA_ONE
&& data[INDEX_TWO] == JPEG_DATA_TWO) {
return ImageType::JPEG;
} else if (data[INDEX_ZERO] == PNG_DATA_ZERO && data[INDEX_ONE] == PNG_DATA_ONE &&
data[INDEX_TWO] == PNG_DATA_TWO && data[INDEX_THREE] == PNG_DATA_THREE) {
return ImageType::PNG;
} else {
return ImageType::WORNG_TYPE;
}
}
bool ImageCompress::GetImageTypeString(const std::unique_ptr<uint8_t[]> &fileData,
size_t fileLength, std::string &imageType)
{
ImageType type = GetImageType(fileData, fileLength);
if (type == ImageType::WORNG_TYPE) {
APP_LOGE("input error type image!");
return false;
}
imageType = type == ImageType::JPEG ? JPEG_FORMAT : PNG_FORMAT;
return true;
}
bool ImageCompress::CompressImageByContent(const std::unique_ptr<uint8_t[]> &fileData, size_t fileSize,
std::unique_ptr<uint8_t[]> &compressedData, int64_t &compressedSize, std::string &imageType)
{
ImageType type = GetImageType(fileData, fileSize);
if (type == ImageType::WORNG_TYPE) {
APP_LOGE("input wrong image!");
return false;
}
imageType = type == ImageType::JPEG ? JPEG_FORMAT : WEBP_FORMAT;
uint32_t errorCode = 0;
Media::SourceOptions options;
std::unique_ptr<Media::ImageSource> imageSourcePtr =
Media::ImageSource::CreateImageSource(fileData.get(), fileSize, options, errorCode);
if (imageSourcePtr == nullptr) {
APP_LOGE("imageSourcePtr nullptr");
return false;
}
Media::DecodeOptions decodeOptions;
uint32_t pixMapError = 0;
std::unique_ptr<Media::PixelMap> pixMap = imageSourcePtr->CreatePixelMap(decodeOptions, pixMapError);
if (pixMap == nullptr || pixMapError != Media::SUCCESS) {
APP_LOGE("CreatePixelMap failed");
return false;
}
double ratio = CalculateRatio(fileSize, imageType);
if (ratio == FILE_SIZE_ERR) {
APP_LOGE("CalculateRatio failed: ratio is %{public}f", ratio);
return false;
}
APP_LOGD("ratio is %{public}f", ratio);
pixMap->scale(ratio, ratio);
Media::ImagePacker imagePacker;
Media::PackOption packOption;
packOption.format = imageType;
packOption.quality = QUALITY;
packOption.numberHint = MUNBER_ONE;
uint8_t *resultBuffer = reinterpret_cast<uint8_t *>(malloc(BUFFER_SIZE));
if (resultBuffer == nullptr) {
APP_LOGE("image packer malloc buffer failed.");
return false;
}
if (imagePacker.StartPacking(resultBuffer, BUFFER_SIZE, packOption) != Media::SUCCESS ||
imagePacker.AddImage(*pixMap) != Media::SUCCESS ||
imagePacker.FinalizePacking(compressedSize) != Media::SUCCESS) {
APP_LOGE_NOFUNC("StartPacking|AddImage|FinalizePacking failed");
return false;
}
compressedData = std::make_unique<uint8_t[]>(compressedSize);
APP_LOGD("compressedSize is %{public}d", static_cast<int32_t>(compressedSize));
uint8_t *result = compressedData.get();
if (memcpy_s(result, compressedSize, resultBuffer, compressedSize) != EOK) {
free(resultBuffer);
APP_LOGE("memcpy_s to compressedData failed!");
return false;
}
free(resultBuffer);
return true;
}
}
}