* Copyright (C) 2024 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 <cmath>
#include "pixel_yuv.h"
#include "image_utils.h"
#include "image_trace.h"
#include "image_type_converter.h"
#include "memory_manager.h"
#include "hitrace_meter.h"
#include "media_errors.h"
#include "pubdef.h"
#include "pixel_yuv_utils.h"
#include "securec.h"
#include "vpe_utils.h"
#include "image_log.h"
#include "image_mdk_common.h"
#include "image_system_properties.h"
#if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
#include "surface_buffer.h"
#endif
#undef LOG_DOMAIN
#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
#undef LOG_TAG
#define LOG_TAG "PixelYuv"
namespace OHOS {
namespace Media {
using namespace std;
static const uint8_t NUM_2 = 2;
static const uint8_t NUM_3 = 3;
static const uint8_t NUM_4 = 4;
static const uint8_t RGBA_BIT_DEPTH = 4;
static const uint32_t TWO_SLICES = 2;
static const uint8_t YUV420_MIN_PIXEL_UINTBYTES = 4;
static const uint8_t YUV420P010_MIN_PIXEL_UINTBYTES = 8;
static const int32_t DEGREES90 = 90;
static const int32_t DEGREES180 = 180;
static const int32_t DEGREES270 = 270;
static const int32_t DEGREES360 = 360;
static const float ROUND_FLOAT_NUMBER = 0.5f;
constexpr uint8_t Y_SHIFT = 16;
constexpr uint8_t U_SHIFT = 8;
constexpr uint8_t V_SHIFT = 0;
constexpr int32_t MAX_DIMENSION = INT32_MAX >> NUM_2;
struct TransInfos {
SkMatrix matrix;
};
struct TransMemoryInfo {
AllocatorType allocType;
std::unique_ptr<AbsMemory> memory = nullptr;
};
static SkImageInfo ToSkImageInfo(ImageInfo &info, sk_sp<SkColorSpace> colorSpace)
{
SkColorType colorType = ImageTypeConverter::ToSkColorType(info.pixelFormat);
SkAlphaType alphaType = ImageTypeConverter::ToSkAlphaType(info.alphaType);
IMAGE_LOGD("ToSkImageInfo w %{public}d, h %{public}d\n" \
"ToSkImageInfo pf %{public}s, at %{public}s, skpf %{public}s, skat %{public}s",
info.size.width, info.size.height,
ImageTypeConverter::ToName(info.pixelFormat).c_str(),
ImageTypeConverter::ToName(info.alphaType).c_str(),
ImageTypeConverter::ToName(colorType).c_str(),
ImageTypeConverter::ToName(alphaType).c_str());
return SkImageInfo::Make(info.size.width, info.size.height, colorType, alphaType, colorSpace);
}
static sk_sp<SkColorSpace> ToSkColorSpace(PixelMap *pixelmap)
{
#ifdef IMAGE_COLORSPACE_FLAG
if (pixelmap->InnerGetGrColorSpacePtr() == nullptr) {
return nullptr;
}
return pixelmap->InnerGetGrColorSpacePtr()->ToSkColorSpace();
#else
return nullptr;
#endif
}
static bool isSameColorSpace(const OHOS::ColorManager::ColorSpace &src,
const OHOS::ColorManager::ColorSpace &dst)
{
auto skSrc = src.ToSkColorSpace();
auto skDst = dst.ToSkColorSpace();
return SkColorSpace::Equals(skSrc.get(), skDst.get());
}
PixelYuv::~PixelYuv()
{
FreePixelMap();
}
const uint8_t *PixelYuv::GetPixel8(int32_t x, int32_t y)
{
IMAGE_LOGE("GetPixel8 is not support on PixelYuv");
return nullptr;
}
const uint16_t *PixelYuv::GetPixel16(int32_t x, int32_t y)
{
IMAGE_LOGE("GetPixel16 is not support on PixelYuv");
return nullptr;
}
const uint32_t *PixelYuv::GetPixel32(int32_t x, int32_t y)
{
IMAGE_LOGE("GetPixel32 is not support on PixelYuv");
return nullptr;
}
bool PixelYuv::GetARGB32Color(int32_t x, int32_t y, uint32_t &color)
{
IMAGE_LOGE("GetARGB32Color is not support on PixelYuv");
return false;
}
uint8_t PixelYuv::GetARGB32ColorA(uint32_t color)
{
IMAGE_LOGE("GetARGB32ColorA is not support on PixelYuv");
return 0;
}
uint8_t PixelYuv::GetARGB32ColorR(uint32_t color)
{
IMAGE_LOGE("GetARGB32ColorR is not support on PixelYuv");
return 0;
}
uint8_t PixelYuv::GetARGB32ColorG(uint32_t color)
{
IMAGE_LOGE("GetARGB32ColorG is not support on PixelYuv");
return 0;
}
uint8_t PixelYuv::GetARGB32ColorB(uint32_t color)
{
IMAGE_LOGE("GetARGB32ColorB is not support on PixelYuv");
return 0;
}
uint32_t PixelYuv::SetAlpha(const float percent)
{
IMAGE_LOGE("SetAlpha is not support on PixelYuv");
return ERR_IMAGE_DATA_UNSUPPORT;
}
uint32_t PixelYuv::getPixelBytesNumber()
{
IMAGE_LOGE("getPixelBytesNumber is not support on PixelYuv");
return ERR_IMAGE_DATA_UNSUPPORT;
}
int32_t PixelYuv::GetByteCount()
{
return PixelMap::GetByteCount();
}
static int32_t GetYSize(int32_t width, int32_t height)
{
return width * height;
}
static int32_t GetUStride(int32_t width)
{
return (width + 1) / NUM_2;
}
static int32_t GetUVHeight(int32_t height)
{
return (height + 1) / NUM_2;
}
bool PixelYuv::YuvRotateConvert(Size &size, int32_t degrees, int32_t &dstWidth, int32_t &dstHeight,
OpenSourceLibyuv::RotationMode &rotateNum)
{
switch (degrees) {
case DEGREES90:
dstWidth = size.height;
dstHeight = size.width;
rotateNum = OpenSourceLibyuv::RotationMode::kRotate90;
return true;
case DEGREES180:
rotateNum = OpenSourceLibyuv::RotationMode::kRotate180;
return true;
case DEGREES270:
dstWidth = size.height;
dstHeight = size.width;
rotateNum = OpenSourceLibyuv::RotationMode::kRotate270;
return true;
default:
return false;
}
}
bool PixelYuv::YuvRotateConvert(Size &srcSize, int32_t degrees, Size &dstSize,
OpenSourceLibyuv::RotationMode &rotateNum, YUVDataInfo yuvDataInfo)
{
switch (degrees) {
case DEGREES90:
dstSize.width = srcSize.height;
dstSize.height = std::max(static_cast<uint32_t>(srcSize.width), yuvDataInfo.yStride);
rotateNum = OpenSourceLibyuv::RotationMode::kRotate90;
return true;
case DEGREES180:
rotateNum = OpenSourceLibyuv::RotationMode::kRotate180;
return true;
case DEGREES270:
dstSize.width = srcSize.height;
dstSize.height = std::max(static_cast<uint32_t>(srcSize.width), yuvDataInfo.yStride);
rotateNum = OpenSourceLibyuv::RotationMode::kRotate270;
return true;
default:
return false;
}
}
std::unique_ptr<AbsMemory> PixelYuv::CreateMemory(PixelFormat pixelFormat, std::string memoryTag, int32_t dstWidth,
int32_t dstHeight, YUVStrideInfo &dstStrides)
{
if (dstWidth <= 0 || dstHeight <= 0) {
IMAGE_LOGE("CreateMemory invalid param, width:%{public}d, height:%{public}d", dstWidth, dstHeight);
return nullptr;
}
if (dstHeight != 0 && dstWidth > (INT32_MAX / dstHeight)) {
IMAGE_LOGE("CreateMemory invalid size (byte count) overflow");
return nullptr;
}
uint32_t pictureSize = GetImageSize(dstWidth, dstHeight, pixelFormat);
int32_t dst_yStride = dstWidth;
int32_t dst_uvStride = (dstWidth + 1) / NUM_2 * NUM_2;
int32_t dst_yOffset = 0;
int32_t dst_uvOffset = dst_yStride * dstHeight;
dstStrides = {dst_yStride, dst_uvStride, dst_yOffset, dst_uvOffset};
MemoryData memoryData = {nullptr, pictureSize, memoryTag.c_str(), {dstWidth, dstHeight}, pixelFormat};
memoryData.usage = GetNoPaddingUsage();
auto m = MemoryManager::CreateMemory(allocatorType_, memoryData);
if (m == nullptr) {
IMAGE_LOGE("CreateMemory failed");
return m;
}
IMAGE_LOGD("CreateMemory allocatorType: %{public}d", allocatorType_);
#if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
if (allocatorType_ == AllocatorType::DMA_ALLOC) {
if (m->extend.data == nullptr) {
IMAGE_LOGE("CreateMemory get surfacebuffer failed");
return m;
}
auto sb = reinterpret_cast<SurfaceBuffer*>(m->extend.data);
ImageUtils::GetYUVStrideInfo(sb, dstStrides);
sptr<SurfaceBuffer> sourceSurfaceBuffer(reinterpret_cast<SurfaceBuffer*>(GetFd()));
sptr<SurfaceBuffer> dstSurfaceBuffer(reinterpret_cast<SurfaceBuffer*>(sb));
VpeUtils::CopySurfaceBufferInfo(sourceSurfaceBuffer, dstSurfaceBuffer);
}
#endif
return m;
}
void PixelYuv::rotate(float degrees)
{
uint32_t errCode = Rotate(degrees);
if (errCode != SUCCESS) {
IMAGE_LOGE("PixelYuv::rotate failed, degrees: %{public}f, ret: %{public}u", degrees, errCode);
}
}
uint32_t PixelYuv::Rotate(float degrees)
{
if (degrees == 0) {
return SUCCESS;
}
if (!IsYuvFormat()) {
return ERR_IMAGE_DATA_UNSUPPORT;
}
if (degrees < 0) {
int n = abs(degrees / DEGREES360);
degrees += DEGREES360 * (n + 1);
}
OpenSourceLibyuv::RotationMode rotateNum = OpenSourceLibyuv::RotationMode::kRotate0;
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
Size dstSize;
dstSize.width = imageInfo_.size.width;
dstSize.height = imageInfo_.size.height;
if (!YuvRotateConvert(imageInfo_.size, degrees, dstSize, rotateNum, yuvDataInfo)) {
IMAGE_LOGI("rotate degress is invalid, don't need rotate");
return SUCCESS;
}
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo_.pixelFormat, "Rotate ImageData",
dstSize.width, dstSize.height, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("rotate CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
uint8_t *dst = reinterpret_cast<uint8_t *>(dstMemory->data.data);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(imageInfo_.pixelFormat),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo};
YuvImageInfo dstInfo;
if (!PixelYuvUtils::YuvRotate(data_, srcInfo, dst, dstInfo, degrees)) {
IMAGE_LOGE("rotate failed");
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
imageInfo_.size.width = dstInfo.width;
imageInfo_.size.height = dstInfo.height;
SetPixelsAddr(dstMemory->data.data, dstMemory->extend.data, dstMemory->data.size, dstMemory->GetType(), nullptr);
SetImageInfo(imageInfo_, true);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
ImageUtils::FlushSurfaceBuffer(this);
return SUCCESS;
}
uint32_t PixelYuv::crop(const Rect &rect)
{
uint32_t errCode = Crop(rect);
if (errCode == ERR_IMAGE_MALLOC_ABNORMAL) {
return ERR_IMAGE_CROP;
}
return errCode;
}
uint32_t PixelYuv::Crop(const Rect &rect)
{
int32_t rectSize = GetYSize(rect.width, rect.height);
int32_t pixelSize = GetYSize(imageInfo_.size.width, imageInfo_.size.height);
if (rect.top < 0 || rect.left < 0 || rectSize > pixelSize || rect.width <= 1 || rect.height <= 1 ||
!IsYuvFormat()) {
IMAGE_LOGE("crop invalid param");
return ERR_IMAGE_INVALID_PARAMETER;
}
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo_.pixelFormat, "crop ImageData", rect.width, rect.height, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("crop CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(imageInfo_.pixelFormat),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo};
if (!PixelYuvUtils::YuvCrop(data_, srcInfo, (uint8_t *)dstMemory->data.data, rect, dstStrides)) {
dstMemory->Release();
return ERR_IMAGE_CROP;
}
SetPixelsAddr(dstMemory->data.data, dstMemory->extend.data,
GetImageSize(rect.width, rect.height, imageInfo_.pixelFormat), dstMemory->GetType(), nullptr);
imageInfo_.size.height = rect.height;
imageInfo_.size.width = rect.width;
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
SetImageInfo(imageInfo_, true);
return SUCCESS;
}
void PixelYuv::scale(float xAxis, float yAxis)
{
ImageTrace imageTrace("PixelMap scale");
scale(xAxis, yAxis, AntiAliasingOption::NONE);
}
void PixelYuv::scale(int32_t dstW, int32_t dstH)
{
ImageTrace imageTrace("PixelMap scale");
scale(dstW, dstH, AntiAliasingOption::NONE);
}
bool PixelYuv::resize(int32_t dstW, int32_t dstH)
{
ImageTrace imageTrace("PixelMap resize");
scale(dstW, dstH, AntiAliasingOption::NONE);
return true;
}
bool PixelYuv::resize(float xAxis, float yAxis)
{
ImageTrace imageTrace("PixelMap resize");
scale(xAxis, yAxis, AntiAliasingOption::NONE);
return true;
}
bool PixelYuv::resizeForPicture(int32_t dstW, int32_t dstH)
{
ImageTrace imageTrace("PixelMap resizeForPicture");
int32_t rowSize = ImageUtils::GetRowDataSizeByPixelFormat(dstW, PixelFormat::NV21);
if (dstH == 0) {
return false;
}
bool cond = rowSize <= 0 || dstH <= 0 || rowSize > std::numeric_limits<int32_t>::max() / dstH;
CHECK_ERROR_RETURN_RET_LOG(cond, false, "%{public}s rowSize: %{public}d, height: %{public}d may overflowed",
__func__, rowSize, dstH);
uint32_t pictureSize = static_cast<uint32_t>(rowSize * dstH);
CHECK_ERROR_RETURN_RET_LOG(SkImageInfo::ByteSizeOverflowed(pictureSize), false,
"%{public}s too large byteCount: %{public}llu", __func__, static_cast<unsigned long long>(pictureSize));
scale(dstW, dstH, AntiAliasingOption::NONE);
return true;
}
void PixelYuv::scale(float xAxis, float yAxis, const AntiAliasingOption &option)
{
uint32_t errCode = Scale(xAxis, yAxis, option);
if (errCode != SUCCESS) {
IMAGE_LOGE("PixelYuv::scale failed, xAxis: %{public}f, yAxis: %{public}f, ret: %{public}u",
xAxis, yAxis, errCode);
}
}
static void SetDstYuvDataInfo(Size size, YUVStrideInfo dstStrides, YUVDataInfo &dstYuvDataInfo)
{
dstYuvDataInfo.imageSize = {size.width, size.height};
dstYuvDataInfo.yWidth = static_cast<uint32_t>(size.width);
dstYuvDataInfo.yHeight = static_cast<uint32_t>(size.height);
dstYuvDataInfo.uvWidth = static_cast<uint32_t>((size.width + 1) / NUM_2);
dstYuvDataInfo.uvHeight = static_cast<uint32_t>(GetUVHeight(size.height));
dstYuvDataInfo.yStride = dstStrides.yStride;
dstYuvDataInfo.uvStride = dstStrides.uvStride;
dstYuvDataInfo.yOffset = dstStrides.yOffset;
dstYuvDataInfo.uvOffset = dstStrides.uvOffset;
}
uint32_t PixelYuv::Scale(float xAxis, float yAxis, AntiAliasingOption option)
{
if (!IsYuvFormat()) {
return ERR_IMAGE_DATA_UNSUPPORT;
}
if (xAxis == 0 || yAxis == 0) {
IMAGE_LOGE("Invalid scale ratio: 0");
return ERR_IMAGE_INVALID_PARAMETER;
}
ImageTrace imageTrace("PixelMap scale");
if (xAxis == 1 && yAxis == 1 && option == AntiAliasingOption::NONE) {
return SUCCESS;
}
ImageInfo imageInfo;
GetImageInfo(imageInfo);
int32_t dstW = (imageInfo.size.width * xAxis + ROUND_FLOAT_NUMBER);
int32_t dstH = (imageInfo.size.height * yAxis + ROUND_FLOAT_NUMBER);
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo.pixelFormat, "scale ImageData", dstW, dstH, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("scale CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
uint8_t *yuvData = reinterpret_cast<uint8_t *>(dstMemory->data.data);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
imageInfo.size.width, imageInfo.size.height, imageInfo_.pixelFormat, yuvDataInfo};
YUVDataInfo dstYuvDataInfo;
SetDstYuvDataInfo({dstW, dstH}, dstStrides, dstYuvDataInfo);
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
dstW, dstH, imageInfo_.pixelFormat, dstYuvDataInfo};
if (PixelYuvUtils::YuvScale(data_, srcInfo, yuvData, dstInfo, PixelYuvUtils::YuvConvertOption(option)) != SUCCESS) {
IMAGE_LOGE("ScaleYuv failed");
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
imageInfo.size.height = dstH;
imageInfo.size.width = dstW;
SetPixelsAddr(dstMemory->data.data, dstMemory->extend.data, dstMemory->data.size, dstMemory->GetType(), nullptr);
SetImageInfo(imageInfo, true);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
ImageUtils::FlushSurfaceBuffer(this);
return SUCCESS;
}
void PixelYuv::scale(int32_t dstW, int32_t dstH, const AntiAliasingOption &option)
{
uint32_t errCode = Scale(dstW, dstH, option);
if (errCode != SUCCESS) {
IMAGE_LOGE("PixelYuv::scale failed, dstW: %{public}d, dstH: %{public}d, ret: %{public}u", dstW, dstH, errCode);
}
}
uint32_t PixelYuv::Scale(int32_t dstW, int32_t dstH, AntiAliasingOption option)
{
if (!IsYuvFormat()) {
return ERR_IMAGE_DATA_UNSUPPORT;
}
ImageInfo imageInfo;
GetImageInfo(imageInfo);
ImageTrace imageTrace("PixelMap scale");
if (imageInfo.size.width == dstW && imageInfo.size.height == dstH && option == AntiAliasingOption::NONE) {
return SUCCESS;
}
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo.pixelFormat, "scale ImageData", dstW, dstH, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("scale CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
uint8_t *yuvData = reinterpret_cast<uint8_t *>(dstMemory->data.data);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
imageInfo.size.width, imageInfo.size.height, imageInfo_.pixelFormat, yuvDataInfo};
YUVDataInfo dstYuvDataInfo;
SetDstYuvDataInfo({dstW, dstH}, dstStrides, dstYuvDataInfo);
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
dstW, dstH, imageInfo_.pixelFormat, dstYuvDataInfo};
if (PixelYuvUtils::YuvScale(data_, srcInfo, yuvData, dstInfo, PixelYuvUtils::YuvConvertOption(option)) != SUCCESS) {
IMAGE_LOGE("ScaleYuv failed");
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
imageInfo.size.height = dstH;
imageInfo.size.width = dstW;
SetPixelsAddr(dstMemory->data.data, dstMemory->extend.data, dstMemory->data.size, dstMemory->GetType(), nullptr);
SetImageInfo(imageInfo, true);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
ImageUtils::FlushSurfaceBuffer(this);
return SUCCESS;
}
void PixelYuv::flip(bool xAxis, bool yAxis)
{
uint32_t errCode = Flip(xAxis, yAxis);
if (errCode != SUCCESS) {
IMAGE_LOGE("PixelYuv::flip failed, xAxis: %{public}d, yAxis: %{public}d, ret: %{public}u",
xAxis, yAxis, errCode);
}
}
uint32_t PixelYuv::Flip(bool xAxis, bool yAxis)
{
if (!IsYuvFormat()) {
return ERR_IMAGE_DATA_UNSUPPORT;
}
if (xAxis == false && yAxis == false) {
return SUCCESS;
}
int32_t srcW = imageInfo_.size.width;
int32_t srcH = imageInfo_.size.height;
PixelFormat format = imageInfo_.pixelFormat;
const uint8_t *src = data_;
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo_.pixelFormat, "flip ImageData", srcW, srcH, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("flip CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
uint8_t *dst = reinterpret_cast<uint8_t *>(dstMemory->data.data);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(format), srcW, srcH, imageInfo_.pixelFormat, yuvDataInfo};
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(format), srcW, srcH, imageInfo_.pixelFormat, yuvDataInfo};
if (xAxis && yAxis) {
if (!PixelYuvUtils::YuvReversal(const_cast<uint8_t *>(src), srcInfo, dst, dstInfo)) {
IMAGE_LOGE("flip yuv xAxis and yAxis failed");
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
} else {
bool isXaxis = ((xAxis | yAxis) && xAxis) ? true : false;
if (!PixelYuvUtils::YuvFlip(const_cast<uint8_t *>(src), srcInfo, dst, isXaxis)) {
IMAGE_LOGE("flip yuv xAxis or yAxis failed");
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
}
SetPixelsAddr(dst, dstMemory->extend.data, dstMemory->data.size, dstMemory->GetType(), nullptr);
return SUCCESS;
}
uint32_t PixelYuv::WritePixels(const uint8_t *source, const uint64_t &bufferSize, const uint32_t &offset,
const uint32_t &stride, const Rect ®ion)
{
if (!CheckPixelsInput(source, bufferSize, offset, region)) {
IMAGE_LOGE("write pixel by rect input parameter fail.");
return ERR_IMAGE_INVALID_PARAMETER;
}
if (!IsEditable()) {
IMAGE_LOGE("write pixel by rect PixelYuv data is not editable.");
return ERR_IMAGE_PIXELMAP_NOT_ALLOW_MODIFY;
}
if (!ImageUtils::IsValidImageInfo(imageInfo_)) {
IMAGE_LOGE("write pixel by rect current PixelYuv image info is invalid.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
if (data_ == nullptr) {
IMAGE_LOGE("write pixel by rect current pixel map data is null.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
int32_t bytesPerPixel = ImageUtils::GetPixelBytes(imageInfo_.pixelFormat);
if (bytesPerPixel == 0) {
IMAGE_LOGE("write pixel by rect get bytes by per pixel fail.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
Position dstPosition{region.left, region.top};
ImageInfo srcInfo;
srcInfo.size.height = region.height;
srcInfo.size.width = region.width;
srcInfo.pixelFormat = imageInfo_.pixelFormat;
if (!PixelYuvUtils::WriteYuvConvert(source + offset, srcInfo, data_, dstPosition, yuvDataInfo_)) {
IMAGE_LOGE("write pixel by rect call WriteYuvConvert fail.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
return SUCCESS;
}
void PixelYuv::GetYUVInfoForCopyPixels(YUVDataInfo &yuvDataInfo)
{
GetImageYUVInfo(yuvDataInfo);
yuvDataInfo.imageSize = imageInfo_.size;
yuvDataInfo.yWidth = static_cast<uint32_t>(imageInfo_.size.width);
yuvDataInfo.yHeight = static_cast<uint32_t>(imageInfo_.size.height);
yuvDataInfo.uvWidth = yuvDataInfo.uvWidth * NUM_2;
if (IsP010Yuv()) {
yuvDataInfo.yWidth = static_cast<uint32_t>(imageInfo_.size.width) * NUM_2;
yuvDataInfo.uvWidth = yuvDataInfo.yWidth;
yuvDataInfo.yOffset = yuvDataInfo.yOffset * NUM_2;
yuvDataInfo.yStride = yuvDataInfo.yStride * NUM_2;
yuvDataInfo.uvOffset = yuvDataInfo.uvOffset * NUM_2;
yuvDataInfo.uvStride = yuvDataInfo.uvStride * NUM_2;
}
}
uint32_t PixelYuv::WritePixels(const uint8_t *source, const uint64_t &bufferSize)
{
if (!IsYuvFormat()) {
IMAGE_LOGE("PixelYuv WritePixels Not YUV format");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
if (source == nullptr || data_ == nullptr) {
IMAGE_LOGE("PixelYuv write pixels by buffer source or current pixelmap data is nullptr");
return ERR_IMAGE_INVALID_PARAMETER;
}
auto requiredBufferSize = GetByteCount();
if (bufferSize < requiredBufferSize) {
IMAGE_LOGE("PixelYuv write pixels by buffer bufferSize error.");
return ERR_IMAGE_INVALID_PARAMETER;
}
if (!IsEditable()) {
IMAGE_LOGE("PixelYuv write pixels by buffer pixelmap data is not editable.");
return ERR_IMAGE_PIXELMAP_NOT_ALLOW_MODIFY;
}
if (!ImageUtils::IsValidImageInfo(imageInfo_)) {
IMAGE_LOGE("PixelYuv write pixels by buffer current pixelmap image info is invalid.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetYUVInfoForCopyPixels(yuvDataInfo);
auto srcY = source;
auto srcUV = source + yuvDataInfo.yWidth * yuvDataInfo.yHeight;
auto dstY = data_ + yuvDataInfo.yOffset;
auto dstUV = data_ + yuvDataInfo.uvOffset;
for (int32_t y = 0; y < static_cast<int32_t>(yuvDataInfo.yHeight); y++) {
if (memcpy_s(dstY, yuvDataInfo.yWidth, srcY, yuvDataInfo.yWidth) != EOK) {
IMAGE_LOGE("PixelYuv write pixels yInfo failed.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
dstY += yuvDataInfo.yStride;
srcY += yuvDataInfo.yWidth;
}
for (int32_t y = 0; y < static_cast<int32_t>(yuvDataInfo.uvHeight); y++) {
if (memcpy_s(dstUV, yuvDataInfo.uvWidth, srcUV, yuvDataInfo.uvWidth) != EOK) {
IMAGE_LOGE("PixelYuv write pixels uvInfo failed.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
dstUV += yuvDataInfo.uvStride;
srcUV += yuvDataInfo.uvWidth;
}
return SUCCESS;
}
uint32_t PixelYuv::ReadPixels(const uint64_t &bufferSize, const uint32_t &offset, const uint32_t &stride,
const Rect ®ion, uint8_t *dst)
{
if (!CheckPixelsInput(dst, bufferSize, offset, region)) {
IMAGE_LOGE("read pixels by rect input parameter fail.");
return ERR_IMAGE_INVALID_PARAMETER;
}
if (data_ == nullptr) {
IMAGE_LOGE("read pixels by rect this pixel data is null.");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
ImageInfo dstImageInfo;
dstImageInfo.size.width = region.width;
dstImageInfo.size.height = region.height;
dstImageInfo.pixelFormat = imageInfo_.pixelFormat;
Position srcPosition{region.left, region.top};
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
YuvImageInfo imageInfo = {PixelYuvUtils::ConvertFormat(imageInfo_.pixelFormat),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo};
if (!PixelYuvUtils::ReadYuvConvert((void *)data_, srcPosition, imageInfo, dst + offset, dstImageInfo)) {
IMAGE_LOGE("read pixels by rect call ReadPixelsConvert fail.");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
return SUCCESS;
}
bool PixelYuv::IsP010Yuv()
{
if (imageInfo_.pixelFormat == PixelFormat::YCBCR_P010 || imageInfo_.pixelFormat == PixelFormat::YCRCB_P010) {
return true;
}
return false;
}
uint32_t PixelYuv::ReadPixels(const uint64_t &bufferSize, uint8_t *dst)
{
if (!IsYuvFormat()) {
IMAGE_LOGE("PixelYuv ReadPixels Not YUV format");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
IMAGE_LOGD("PixelYuv ReadPixels by bufferSize");
if (dst == nullptr) {
IMAGE_LOGE("PixelYuv read pixels by buffer input dst address is null.");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
if (data_ == nullptr) {
IMAGE_LOGE("PixelYuv read pixels by buffer current PixelMap data is null.");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
auto requiredBufferSize = GetByteCount();
IMAGE_LOGD("PixelYuv requiredBufferSize: %{public}d", requiredBufferSize);
if (bufferSize < static_cast<uint64_t>(requiredBufferSize)) {
IMAGE_LOGE("PixelYuv read pixels dst buffer(%{public}llu) < current pixelmap size(%{public}u).",
static_cast<unsigned long long>(bufferSize), requiredBufferSize);
return ERR_IMAGE_INVALID_PARAMETER;
}
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetYUVInfoForCopyPixels(yuvDataInfo);
uint8_t *srcUVPixels = data_ + yuvDataInfo.uvOffset;
uint8_t *srcYPixels = data_ + yuvDataInfo.yOffset;
for (int32_t y = 0; y<static_cast<int32_t>(yuvDataInfo.yHeight); y++) {
if (memcpy_s(dst, yuvDataInfo.yWidth, srcYPixels, yuvDataInfo.yWidth) != EOK) {
IMAGE_LOGE("PixelYuv read pixels yInfo failed.");
return ERR_IMAGE_INVALID_PARAMETER;
}
dst += yuvDataInfo.yWidth;
srcYPixels += yuvDataInfo.yStride;
}
for (int32_t y = 0; y<static_cast<int32_t>(yuvDataInfo.uvHeight); y++) {
if (memcpy_s(dst, yuvDataInfo.uvWidth, srcUVPixels, yuvDataInfo.uvWidth) != EOK) {
IMAGE_LOGE("PixelYuv read pixels uvInfo failed.");
return ERR_IMAGE_INVALID_PARAMETER;
}
dst += yuvDataInfo.uvWidth;
srcUVPixels += yuvDataInfo.uvStride;
}
return SUCCESS;
}
void PixelYuv::translate(float xAxis, float yAxis)
{
uint32_t errCode = Translate(xAxis, yAxis);
if (errCode != SUCCESS) {
IMAGE_LOGE("PixelYuv::translate failed, xAxis: %{public}f, yAxis: %{public}f, ret: %{public}u",
xAxis, yAxis, errCode);
}
}
uint32_t PixelYuv::Translate(float xAxis, float yAxis)
{
if (!IsYuvFormat()) {
IMAGE_LOGE("translate not yuv format");
return ERR_IMAGE_DATA_UNSUPPORT;
}
if (!PixelYuvUtils::IsLegalAxis(xAxis, yAxis, imageInfo_)) {
return ERR_IMAGE_INVALID_PARAMETER;
}
int32_t width = imageInfo_.size.width + xAxis;
int32_t height = imageInfo_.size.height + yAxis;
PixelFormat format = imageInfo_.pixelFormat;
YUVStrideInfo dstStrides;
auto dstMemory = CreateMemory(imageInfo_.pixelFormat, "translate ImageData", width, height, dstStrides);
if (dstMemory == nullptr) {
IMAGE_LOGE("translate CreateMemory failed");
return ERR_IMAGE_MALLOC_ABNORMAL;
}
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
XYaxis xyAxis = {xAxis, yAxis};
uint8_t *dst = reinterpret_cast<uint8_t *>(dstMemory->data.data);
PixelYuvUtils::SetTranslateDataDefault(dst, width, height, format, dstStrides);
if (!PixelYuvUtils::YuvTranslate(data_, yuvDataInfo, dst, xyAxis, imageInfo_, dstStrides)) {
dstMemory->Release();
return ERR_IMAGE_TRANSFORM;
}
imageInfo_.size.width = width;
imageInfo_.size.height = height;
uint32_t dstSize = GetImageSize(width, height, format);
SetPixelsAddr(dst, dstMemory->extend.data, dstSize, dstMemory->GetType(), nullptr);
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
return SUCCESS;
}
uint32_t PixelYuv::ReadPixel(const Position &pos, uint32_t &dst)
{
if (pos.x < 0 || pos.y < 0) {
IMAGE_LOGE("read pixel by pos input invalid exception. [x(%{public}d), y(%{public}d)]", pos.x, pos.y);
return ERR_IMAGE_INVALID_PARAMETER;
}
if (data_ == nullptr) {
IMAGE_LOGE("read pixel by pos source data is null.");
return ERR_IMAGE_READ_PIXELMAP_FAILED;
}
ColorYuv420 colorYuv = GetYuv420Color(abs(pos.x), abs(pos.y));
dst = (colorYuv.colorY << Y_SHIFT) | (colorYuv.colorU << U_SHIFT) | (colorYuv.colorV << V_SHIFT);
return SUCCESS;
}
bool PixelYuv::WritePixels(const uint32_t &color)
{
if (!IsYuvFormat() || data_ == nullptr) {
IMAGE_LOGE("erase pixels by color current pixel map data is null.");
return false;
}
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
return PixelYuvUtils::Yuv420WritePixels(yuvDataInfo_, data_, imageInfo_, color);
}
uint32_t PixelYuv::WritePixel(const Position &pos, const uint32_t &color)
{
if (!IsYuvFormat() || (pos.x < 0 || pos.y < 0)) {
IMAGE_LOGE("write pixel by pos but input position is invalid. [x(%{public}d), y(%{public}d)]", pos.x, pos.y);
return ERR_IMAGE_INVALID_PARAMETER;
}
if (data_ == nullptr) {
IMAGE_LOGE("write pixel by pos but current pixelmap data is nullptr.");
return ERR_IMAGE_WRITE_PIXELMAP_FAILED;
}
return PixelYuvUtils::YuvWritePixel(data_, yuvDataInfo_, imageInfo_.pixelFormat, pos, color);
}
ColorYuv420 PixelYuv::GetYuv420Color(uint32_t x, uint32_t y)
{
ColorYuv420 colorYuv;
ImageInfo imageInfo;
GetImageInfo(imageInfo);
PixelFormat format = imageInfo.pixelFormat;
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
colorYuv.colorY = PixelYuvUtils::GetYuv420Y(x, y, yuvDataInfo, data_);
colorYuv.colorU = PixelYuvUtils::GetYuv420U(x, y, yuvDataInfo, format, data_);
colorYuv.colorV = PixelYuvUtils::GetYuv420V(x, y, yuvDataInfo, format, data_);
return colorYuv;
}
void PixelYuv::SetPixelsAddr(void *addr, void *context, uint32_t size, AllocatorType type, CustomFreePixelMap func)
{
if (data_ != nullptr) {
IMAGE_LOGD("SetPixelsAddr release the existed data first");
FreePixelMap();
}
if (type == AllocatorType::SHARE_MEM_ALLOC && context == nullptr) {
IMAGE_LOGE("SetPixelsAddr error type %{public}d ", type);
}
data_ = static_cast<uint8_t *>(addr);
context_ = context;
pixelsSize_ = size;
allocatorType_ = type;
custFreePixelMap_ = func;
if (type == AllocatorType::DMA_ALLOC && rowDataSize_ != 0) {
SetImageInfo(imageInfo_, true);
}
ImageUtils::FlushSurfaceBuffer(this);
}
static bool IsYUVP010Format(PixelFormat format)
{
return format == PixelFormat::YCBCR_P010 || format == PixelFormat::YCRCB_P010;
}
bool PixelYuv::CheckPixelsInput(const uint8_t *dst, const uint64_t &bufferSize, const uint32_t &offset,
const Rect ®ion)
{
if (dst == nullptr) {
IMAGE_LOGE("CheckPixelsInput input dst address is null.");
return false;
}
if (bufferSize == 0) {
IMAGE_LOGE("CheckPixelsInput input buffer size is 0.");
return false;
}
if (region.left < 0 || region.top < 0 || static_cast<uint64_t>(offset) > bufferSize) {
IMAGE_LOGE("CheckPixelsInput left(%{public}d) or top(%{public}d) or offset(%{public}u) < 0.",
region.left, region.top, offset);
return false;
}
if (region.width <= 1 || region.height <= 1 || region.width > MAX_DIMENSION || region.height > MAX_DIMENSION) {
IMAGE_LOGE("CheckPixelsInput width(%{public}d) or height(%{public}d) is < 0.", region.width,
region.height);
return false;
}
if (region.left > GetWidth() - region.width) {
IMAGE_LOGE("CheckPixelsInput left(%{public}d) + width(%{public}d) is > PixelYuv width(%{public}d).",
region.left, region.width, GetWidth());
return false;
}
if (region.top > GetHeight() - region.height) {
IMAGE_LOGE("CheckPixelsInput top(%{public}d) + height(%{public}d) is > PixelYuv height(%{public}d).",
region.top, region.height, GetHeight());
return false;
}
if (IsYUVP010Format(imageInfo_.pixelFormat)) {
if ((static_cast<uint64_t>(offset) + YUV420P010_MIN_PIXEL_UINTBYTES) >= bufferSize) {
IMAGE_LOGE(
"CheckPixelsInput fail, height(%{public}d), width(%{public}d) "
"offset(%{public}u), bufferSize:%{public}llu.",
region.height, region.width, offset,
static_cast<unsigned long long>(bufferSize));
return false;
}
} else {
if ((static_cast<uint64_t>(offset) + YUV420_MIN_PIXEL_UINTBYTES) >= bufferSize) {
IMAGE_LOGE(
"CheckPixelsInput fail, height(%{public}d), width(%{public}d) "
"offset(%{public}u), bufferSize:%{public}llu.",
region.height, region.width, offset,
static_cast<unsigned long long>(bufferSize));
return false;
}
}
return true;
}
void PixelYuv::SetRowDataSizeForImageInfo(ImageInfo info)
{
rowDataSize_ = info.size.width * NUM_3 / NUM_2;
if (IsYUVP010Format(info.pixelFormat)) {
rowDataSize_ *= NUM_2;
}
return;
}
uint32_t PixelYuv::GetImageSize(int32_t width, int32_t height, PixelFormat format)
{
uint32_t size = static_cast<uint32_t>(GetYSize(width, height) +
GetUStride(width) * GetUVHeight(height) * TWO_SLICES);
if (IsYUVP010Format(format)) {
size *= NUM_2;
}
return size;
}
#ifdef IMAGE_COLORSPACE_FLAG
uint32_t PixelYuv::SetColorSpace(const OHOS::ColorManager::ColorSpace &grColorSpace, SkTransYuvInfo &src,
PixelFormat &format, uint64_t rowStride)
{
int32_t width = static_cast<int32_t>(yuvDataInfo_.yStride);
int32_t height = static_cast<int32_t>(yuvDataInfo_.yHeight);
if (!PixelYuvUtils::CheckWidthAndHeightMult(width, height, RGBA_BIT_DEPTH)) {
IMAGE_LOGE("SetColorSpace size overflow width(%{public}d), height(%{public}d)", width, height);
return ERR_IMAGE_COLOR_CONVERT;
}
SkTransYuvInfo dst;
dst.info = ToSkImageInfo(imageInfo_, grColorSpace.ToSkColorSpace());
MemoryData memoryData = {nullptr, width * height * NUM_4, "ApplyColorSpace ImageData",
{dst.info.width(), dst.info.height()}};
memoryData.usage = GetNoPaddingUsage();
auto dstMemory = MemoryManager::CreateMemory(allocatorType_, memoryData);
if (dstMemory == nullptr) {
IMAGE_LOGE("applyColorSpace CreateMemory failed");
return ERR_IMAGE_COLOR_CONVERT;
}
if (!src.bitmap.readPixels(dst.info, dstMemory->data.data, rowStride, 0, 0)) {
dstMemory->Release();
IMAGE_LOGE("ReadPixels failed");
return ERR_IMAGE_COLOR_CONVERT;
}
uint8_t *bgraData = reinterpret_cast<uint8_t *>(dstMemory->data.data);
if (ColorSpaceBGRAToYuv(bgraData, dst, imageInfo_, format, grColorSpace) != SUCCESS) {
dstMemory->Release();
IMAGE_LOGE("ColorSpaceBGRAToYuv failed");
return ERR_IMAGE_COLOR_CONVERT;
}
SetImageInfo(imageInfo_, true);
return SUCCESS;
}
static void ToImageInfo(ImageInfo &info, SkImageInfo &skInfo, bool sizeOnly = true)
{
info.size.width = skInfo.width();
info.size.height = skInfo.height();
if (!sizeOnly) {
info.alphaType = ImageTypeConverter::ToAlphaType(skInfo.alphaType());
info.pixelFormat = ImageTypeConverter::ToPixelFormat(skInfo.colorType());
}
}
bool PixelYuv::CheckColorSpace(const OHOS::ColorManager::ColorSpace &grColorSpace)
{
auto grName = grColorSpace.GetColorSpaceName();
if (grColorSpace_ != nullptr && isSameColorSpace(*grColorSpace_, grColorSpace)) {
if (grColorSpace_->GetColorSpaceName() != grName) {
InnerSetColorSpace(grColorSpace);
IMAGE_LOGI("applyColorSpace inner set");
}
return true;
}
return false;
}
int32_t PixelYuv::ColorSpaceBGRAToYuv(uint8_t *bgraData, SkTransYuvInfo &dst, ImageInfo &imageInfo,
PixelFormat &format, const OHOS::ColorManager::ColorSpace &grColorSpace)
{
int32_t dstWidth = dst.info.width();
int32_t dstHeight = dst.info.height();
uint32_t pictureSize = GetImageSize(dstWidth, dstHeight, format);
MemoryData memoryYuvData = {nullptr, pictureSize, "Trans ImageData", {dstWidth, dstHeight}};
memoryYuvData.usage = GetNoPaddingUsage();
auto yuvMemory = MemoryManager::CreateMemory(allocatorType_, memoryYuvData);
if (yuvMemory == nullptr) {
IMAGE_LOGE("applyColorSpace CreateYuvMemory failed");
return ERR_IMAGE_COLOR_CONVERT;
}
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(PixelFormat::BGRA_8888),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo_};
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(format),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo_};
if (!PixelYuvUtils::BGRAToYuv420(bgraData, srcInfo, reinterpret_cast<uint8_t *>(yuvMemory->data.data), dstInfo)) {
IMAGE_LOGE("applyColorSpace BGRAToYuv420 failed");
yuvMemory->Release();
return ERR_IMAGE_COLOR_CONVERT;
}
imageInfo.pixelFormat = format;
dst.info = ToSkImageInfo(imageInfo, grColorSpace.ToSkColorSpace());
ToImageInfo(imageInfo, dst.info);
auto grName = grColorSpace.GetColorSpaceName();
grColorSpace_ = std::make_shared<OHOS::ColorManager::ColorSpace>(dst.info.refColorSpace(), grName);
SetPixelsAddr(reinterpret_cast<void *>(yuvMemory->data.data), yuvMemory->extend.data, pictureSize,
yuvMemory->GetType(), nullptr);
return SUCCESS;
}
uint32_t PixelYuv::ApplyColorSpace(const OHOS::ColorManager::ColorSpace &grColorSpace)
{
if (CheckColorSpace(grColorSpace)) {
return SUCCESS;
}
PixelFormat format = imageInfo_.pixelFormat;
imageInfo_.pixelFormat = PixelFormat::BGRA_8888;
SkTransYuvInfo src;
src.info = ToSkImageInfo(imageInfo_, ToSkColorSpace(this));
uint64_t rowStride = src.info.minRowBytes();
uint8_t *srcData = data_;
#if !defined(_WIN32) && !defined(_APPLE) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
if (GetAllocatorType() == AllocatorType::DMA_ALLOC && GetFd() != nullptr) {
SurfaceBuffer *sbBuffer = reinterpret_cast<SurfaceBuffer *>(GetFd());
rowStride = static_cast<uint64_t>(sbBuffer->GetStride());
}
srcData = static_cast<uint8_t *>(GetWritablePixels());
#endif
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(*this);
#endif
YUVDataInfo yuvDataInfo;
GetImageYUVInfo(yuvDataInfo);
int32_t width = static_cast<int32_t>(yuvDataInfo.yStride);
int32_t height = static_cast<int32_t>(yuvDataInfo.yHeight);
if (!PixelYuvUtils::CheckWidthAndHeightMult(width, height, RGBA_BIT_DEPTH)) {
IMAGE_LOGE("ApplyColorSpace size overflow width(%{public}d), height(%{public}d)", width, height);
return ERR_IMAGE_COLOR_CONVERT;
}
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(format),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo};
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(PixelFormat::BGRA_8888),
imageInfo_.size.width, imageInfo_.size.height, imageInfo_.pixelFormat, yuvDataInfo};
std::unique_ptr<uint8_t[]> RGBAdata = std::make_unique<uint8_t[]>(width * height * NUM_4);
if (!PixelYuvUtils::Yuv420ToBGRA(srcData, srcInfo, RGBAdata.get(), dstInfo)) {
IMAGE_LOGE("applyColorSpace Yuv420ToBGRA failed");
return ERR_IMAGE_COLOR_CONVERT;
}
src.bitmap.installPixels(src.info, RGBAdata.get(), rowStride);
return SetColorSpace(grColorSpace, src, format, rowStride);
}
#endif
struct YUVPlaneCopyInfo {
const uint8_t *srcBase = nullptr;
uint8_t *dstBase = nullptr;
uint64_t srcOffset = 0;
uint64_t dstOffset = 0;
uint64_t srcStride = 0;
uint64_t dstStride = 0;
uint64_t rowBytes = 0;
uint32_t height = 0;
const char* planeName = nullptr;
};
static bool CopyYuvPlaneRows(const YUVPlaneCopyInfo ©Info)
{
for (uint32_t row = 0; row < copyInfo.height; ++row) {
const uint8_t *srcRow = copyInfo.srcBase + copyInfo.srcOffset + row * copyInfo.srcStride;
uint8_t *dstRow = copyInfo.dstBase + copyInfo.dstOffset + row * copyInfo.dstStride;
if (memcpy_s(dstRow, static_cast<size_t>(copyInfo.dstStride),
srcRow, static_cast<size_t>(copyInfo.rowBytes)) != EOK) {
IMAGE_LOGE("CopyYuvPlanes %{public}s plane copy failed", copyInfo.planeName);
return false;
}
}
return true;
}
static bool CopyYuvPlanes(PixelMap &source, PixelMap &dstPixelMap)
{
ImageInfo srcInfo;
ImageInfo dstInfo;
source.GetImageInfo(srcInfo);
dstPixelMap.GetImageInfo(dstInfo);
bool cond = !ImageUtils::IsYuvFormat(srcInfo.pixelFormat) || srcInfo.pixelFormat != dstInfo.pixelFormat;
CHECK_ERROR_RETURN_RET(cond, false);
cond = (source.GetPixels() == nullptr || dstPixelMap.GetPixels() == nullptr);
CHECK_ERROR_RETURN_RET(cond, false);
cond = (srcInfo.size.width != dstInfo.size.width || srcInfo.size.height != dstInfo.size.height);
CHECK_ERROR_RETURN_RET(cond, false);
YUVDataInfo srcYuv {};
YUVDataInfo dstYuv {};
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(source);
ImageUtils::UpdateYUVDataInfo(dstPixelMap);
#endif
source.GetImageYUVInfo(srcYuv);
dstPixelMap.GetImageYUVInfo(dstYuv);
const bool isP010 = (srcInfo.pixelFormat == PixelFormat::YCBCR_P010 ||
srcInfo.pixelFormat == PixelFormat::YCRCB_P010);
const uint32_t bytesPerSample = isP010 ? 2 : 1;
auto srcBase = const_cast<uint8_t *>(source.GetPixels());
auto dstBase = const_cast<uint8_t *>(dstPixelMap.GetPixels());
const uint32_t srcCapacity = source.GetAllocationByteCount();
const uint32_t dstCapacity = dstPixelMap.GetAllocationByteCount();
const uint64_t srcYStrideBytes = static_cast<uint64_t>(srcYuv.yStride) * bytesPerSample;
const uint64_t srcUvStrideBytes = static_cast<uint64_t>(srcYuv.uvStride) * bytesPerSample;
const uint64_t dstYStrideBytes = static_cast<uint64_t>(dstYuv.yStride) * bytesPerSample;
const uint64_t dstUvStrideBytes = static_cast<uint64_t>(dstYuv.uvStride) * bytesPerSample;
const uint64_t srcYOffsetBytes = static_cast<uint64_t>(srcYuv.yOffset) * bytesPerSample;
const uint64_t srcUvOffsetBytes = static_cast<uint64_t>(srcYuv.uvOffset) * bytesPerSample;
const uint64_t dstYOffsetBytes = static_cast<uint64_t>(dstYuv.yOffset) * bytesPerSample;
const uint64_t dstUvOffsetBytes = static_cast<uint64_t>(dstYuv.uvOffset) * bytesPerSample;
const uint64_t yRowBytes = static_cast<uint64_t>(srcInfo.size.width) * bytesPerSample;
const uint64_t uvRowBytes = static_cast<uint64_t>(srcYuv.uvWidth) * 2ULL * bytesPerSample;
const uint64_t yPlaneBytes = srcYStrideBytes * static_cast<uint64_t>(srcYuv.yHeight);
const uint64_t uvPlaneBytes = srcUvStrideBytes * static_cast<uint64_t>(srcYuv.uvHeight);
cond = (srcYOffsetBytes + yPlaneBytes > srcCapacity || srcUvOffsetBytes + uvPlaneBytes > srcCapacity);
CHECK_ERROR_RETURN_RET_LOG(cond, false, "CopyYuvPlanes source out of range, capacity=%{public}u", srcCapacity);
cond = (dstYOffsetBytes + dstYStrideBytes * static_cast<uint64_t>(dstYuv.yHeight) > dstCapacity ||
dstUvOffsetBytes + dstUvStrideBytes * static_cast<uint64_t>(dstYuv.uvHeight) > dstCapacity);
CHECK_ERROR_RETURN_RET_LOG(cond, false, "CopyYuvPlanes destination out of range, capacity=%{public}u", dstCapacity);
cond = (yRowBytes > srcYStrideBytes || yRowBytes > dstYStrideBytes || uvRowBytes > srcUvStrideBytes ||
uvRowBytes > dstUvStrideBytes);
CHECK_ERROR_RETURN_RET_LOG(cond, false, "CopyYuvPlanes invalid stride for yuv copy");
YUVPlaneCopyInfo yCopyInfo = {srcBase, dstBase, srcYOffsetBytes, dstYOffsetBytes,
srcYStrideBytes, dstYStrideBytes, yRowBytes, srcYuv.yHeight, "y"};
YUVPlaneCopyInfo uvCopyInfo = {srcBase, dstBase, srcUvOffsetBytes, dstUvOffsetBytes,
srcUvStrideBytes, dstUvStrideBytes, uvRowBytes, srcYuv.uvHeight, "uv"};
if (!CopyYuvPlaneRows(yCopyInfo) || !CopyYuvPlaneRows(uvCopyInfo)) {
return false;
}
ImageUtils::FlushSurfaceBuffer(&dstPixelMap);
return true;
}
std::unique_ptr<PixelMap> PixelYuv::clone(int32_t &errorCode)
{
if (!IsYuvFormat()) {
errorCode = ERR_IMAGE_READ_PIXELMAP_FAILED;
return nullptr;
}
if (imageInfo_.size.width <= 0 || imageInfo_.size.height <= 0) {
errorCode = ERR_IMAGE_INVALID_PARAMETER;
return nullptr;
}
InitializationOptions opts;
opts.srcPixelFormat = imageInfo_.pixelFormat;
opts.pixelFormat = imageInfo_.pixelFormat;
opts.alphaType = imageInfo_.alphaType;
opts.size = imageInfo_.size;
opts.srcRowStride = rowStride_;
opts.editable = IsEditable();
opts.useDMA = (GetAllocatorType() == AllocatorType::DMA_ALLOC);
auto dstPixelMap = PixelMap::Create(opts);
if (dstPixelMap == nullptr) {
errorCode = ERR_IMAGE_INVALID_PARAMETER;
return nullptr;
}
if (!CopyYuvPlanes(*this, *dstPixelMap.get())) {
errorCode = ERR_IMAGE_MALLOC_ABNORMAL;
return nullptr;
}
dstPixelMap->SetTransformered(IsTransformered());
TransformData transformData;
GetTransformData(transformData);
dstPixelMap->SetTransformData(transformData);
dstPixelMap->SetHdrType(GetHdrType());
dstPixelMap->SetHdrMetadata(GetHdrMetadata());
#ifdef IMAGE_COLORSPACE_FLAG
OHOS::ColorManager::ColorSpace colorspace = InnerGetGrColorSpace();
dstPixelMap->InnerSetColorSpace(colorspace);
#endif
errorCode = SUCCESS;
return dstPixelMap;
}
static void GetYuvDataInfo(PixelMap &source, YUVDataInfo &yuvDataInfo)
{
#if !defined(CROSS_PLATFORM)
CHECK_ERROR_RETURN_LOG(source.GetAllocatorType() != AllocatorType::DMA_ALLOC,
"GetYuvDataInfo source is not DMA allocator");
SurfaceBuffer *sb = reinterpret_cast<SurfaceBuffer *>(source.GetFd());
CHECK_ERROR_RETURN_LOG(sb == nullptr, "GetYuvDataInfo source pixelMap etFd failed");
YUVStrideInfo strideInfo;
ImageUtils::GetYUVStrideInfo(sb, strideInfo);
if (strideInfo.yStride > 0 && strideInfo.uvStride > 0) {
yuvDataInfo.yStride = strideInfo.yStride;
yuvDataInfo.uvStride = strideInfo.uvStride;
yuvDataInfo.yOffset = strideInfo.yOffset;
yuvDataInfo.uvOffset = strideInfo.uvOffset;
IMAGE_LOGD("GetYuvDataInfo use DMA stride: yStride=%{public}d, uvStride=%{public}d",
yuvDataInfo.yStride, yuvDataInfo.uvStride);
}
#endif
}
std::unique_ptr<PixelMap> CreateDstPixelMap(PixelMap &source, Size targetSize, std::unique_ptr<AbsMemory> dstMemory,
YUVStrideInfo dstStrides, int32_t &errorCode)
{
ImageInfo imageInfo;
source.GetImageInfo(imageInfo);
InitializationOptions opts;
opts.srcPixelFormat = imageInfo.pixelFormat;
opts.pixelFormat = imageInfo.pixelFormat;
opts.alphaType = imageInfo.alphaType;
opts.size = {targetSize.width, targetSize.height};
opts.editable = source.IsEditable();
opts.useDMA = (source.GetAllocatorType() == AllocatorType::DMA_ALLOC);
auto dstPixelMap = PixelMap::Create(opts);
if (dstPixelMap == nullptr) {
dstMemory->Release();
errorCode = ERR_IMAGE_INVALID_PARAMETER;
return nullptr;
}
dstPixelMap->SetPixelsAddr(dstMemory->data.data, dstMemory->extend.data, dstMemory->data.size,
dstMemory->GetType(), nullptr);
imageInfo.size.width = targetSize.width;
imageInfo.size.height = targetSize.height;
if (dstPixelMap->SetImageInfo(imageInfo, true) != SUCCESS) {
dstMemory->Release();
errorCode = ERR_IMAGE_DATA_ABNORMAL;
return nullptr;
}
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(dstPixelMap);
#endif
ImageUtils::FlushSurfaceBuffer(dstPixelMap.get());
dstPixelMap->SetTransformered(source.IsTransformered());
TransformData transformData;
source.GetTransformData(transformData);
dstPixelMap->SetTransformData(transformData);
dstPixelMap->SetHdrType(ImageHdrType::SDR);
dstPixelMap->SetHdrMetadata(nullptr);
#ifdef IMAGE_COLORSPACE_FLAG
OHOS::ColorManager::ColorSpace colorspace = source.InnerGetGrColorSpace();
dstPixelMap->InnerSetColorSpace(colorspace);
#endif
errorCode = SUCCESS;
return dstPixelMap;
}
std::unique_ptr<PixelMap> PixelYuv::CreateThumbnailPixelMap(PixelMap &source, int32_t maxPixelSize, int32_t &errorCode)
{
ImageInfo imageInfo;
source.GetImageInfo(imageInfo);
if (!ImageUtils::IsYuvFormat(imageInfo.pixelFormat) || imageInfo.size.width <= 0 || imageInfo.size.height <= 0) {
errorCode = ERR_IMAGE_INVALID_PARAMETER;
return nullptr;
}
Size targetSize;
float scale = 1.0f;
uint32_t err = ImageUtils::GetThumbnailScaleTargetSize(imageInfo.size, maxPixelSize, targetSize, scale);
errorCode = static_cast<int32_t>(err);
CHECK_ERROR_RETURN_RET(errorCode != SUCCESS, nullptr);
if (ImageUtils::FloatEqual(scale, 1.0f)) {
return source.clone(errorCode);
}
targetSize.width = targetSize.width - (targetSize.width % NUM_2);
targetSize.height = targetSize.height - (targetSize.height % NUM_2);
YUVStrideInfo dstStrides;
void *srcSurfaceBuffer = nullptr;
#if !defined(CROSS_PLATFORM)
if (source.GetAllocatorType() == AllocatorType::DMA_ALLOC) {
srcSurfaceBuffer = reinterpret_cast<void *>(source.GetFd());
}
#endif
auto dstMemory = PixelYuvUtils::CreateYuvMemory(imageInfo.pixelFormat, "thumbnail ImageData", targetSize.width,
targetSize.height, source.GetAllocatorType(), source.GetNoPaddingUsage(), srcSurfaceBuffer, dstStrides);
if (dstMemory == nullptr || dstMemory->data.data == nullptr) {
errorCode = ERR_IMAGE_MALLOC_ABNORMAL;
return nullptr;
}
YUVDataInfo yuvDataInfo;
#if !defined(CROSS_PLATFORM)
ImageUtils::UpdateYUVDataInfo(source);
#endif
source.GetImageYUVInfo(yuvDataInfo);
GetYuvDataInfo(source, yuvDataInfo);
YUVDataInfo dstYuvDataInfo = yuvDataInfo;
SetDstYuvDataInfo(targetSize, dstStrides, dstYuvDataInfo);
YuvImageInfo srcInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
imageInfo.size.width, imageInfo.size.height, imageInfo.pixelFormat, yuvDataInfo};
YuvImageInfo dstInfo = {PixelYuvUtils::ConvertFormat(imageInfo.pixelFormat),
targetSize.width, targetSize.height, imageInfo.pixelFormat, dstYuvDataInfo};
uint8_t *dstData = reinterpret_cast<uint8_t *>(dstMemory->data.data);
int32_t ret = PixelYuvUtils::YuvScale(const_cast<uint8_t *>(source.GetPixels()), srcInfo, dstData, dstInfo,
PixelYuvUtils::YuvConvertOption(AntiAliasingOption::HIGH));
if (ret != SUCCESS) {
dstMemory->Release();
errorCode = ERR_IMAGE_DATA_ABNORMAL;
return nullptr;
}
return CreateDstPixelMap(source, targetSize, std::move(dstMemory), dstStrides, errorCode);
}
}
}