#if __APPLE__
#include "ccap_convert_apple.h"
#include "ccap_utils.h"
#include <Accelerate/Accelerate.h>
#include <cassert>
namespace ccap {
template <int inputChannels, int outputChannels, bool swapRB>
void colorShuffle_apple(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height) {
static_assert((inputChannels == 3 || inputChannels == 4) && (outputChannels == 3 || outputChannels == 4),
"inputChannels and outputChannels must be 3 or 4");
assert(width > 0 && height != 0);
vImage_Buffer dstBuffer;
std::shared_ptr<ccap::Allocator> sharedAllocator;
if (height > 0) {
dstBuffer = { dst, (uint32_t)height, (uint32_t)width, (size_t)dstStride };
} else {
height = -height; sharedAllocator = ccap::getSharedAllocator();
assert(sharedAllocator != nullptr);
if (dstStride * height > sharedAllocator->size()) {
sharedAllocator->resize(dstStride * height);
}
dstBuffer = { sharedAllocator->data(), (uint32_t)height, (uint32_t)width, (size_t)dstStride };
}
vImage_Buffer srcBuffer = { (void*)src, (uint32_t)height, (uint32_t)width, (size_t)srcStride };
if constexpr (inputChannels == outputChannels) {
static_assert(swapRB, "swapRB must be true when inputChannels == outputChannels");
constexpr uint8_t permuteMap[4] = { 2, 1, 0, 3 };
if constexpr (inputChannels == 4) { vImagePermuteChannels_ARGB8888(&srcBuffer, &dstBuffer, permuteMap, kvImageNoFlags);
} else {
vImagePermuteChannels_RGB888(&srcBuffer, &dstBuffer, permuteMap, kvImageNoFlags);
}
} else {
if constexpr (inputChannels == 4) { if constexpr (swapRB) { vImageConvert_RGBA8888toBGR888(&srcBuffer, &dstBuffer, kvImageNoFlags);
} else { vImageConvert_RGBA8888toRGB888(&srcBuffer, &dstBuffer, kvImageNoFlags);
}
} else { if constexpr (swapRB) { vImageConvert_RGB888toBGRA8888(&srcBuffer, nullptr, 0xff, &dstBuffer, false, kvImageNoFlags);
} else { vImageConvert_RGB888toRGBA8888(&srcBuffer, nullptr, 0xff, &dstBuffer, false, kvImageNoFlags);
}
}
}
if (sharedAllocator) { verticalFlip_apple((const uint8_t*)dstBuffer.data, dstStride, dst, dstStride, height);
}
}
template void colorShuffle_apple<4, 4, true>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
template void colorShuffle_apple<4, 3, true>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
template void colorShuffle_apple<4, 3, false>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
template void colorShuffle_apple<3, 4, true>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
template void colorShuffle_apple<3, 4, false>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
template void colorShuffle_apple<3, 3, true>(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int width, int height);
void verticalFlip_apple(const uint8_t* src, int srcStride, uint8_t* dst, int dstStride, int height) {
assert(src != nullptr && dst != nullptr);
assert(srcStride > 0 && dstStride > 0 && height > 0); vImage_Buffer srcBuffer = { (void*)src, (vImagePixelCount)height, (vImagePixelCount)srcStride, (size_t)srcStride };
vImage_Buffer dstBuffer = { dst, (vImagePixelCount)height, (vImagePixelCount)dstStride, (size_t)dstStride };
vImageVerticalReflect_Planar8(&srcBuffer, &dstBuffer, kvImageNoFlags);
}
void nv12ToArgb32_apple_imp(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height,
const vImage_YpCbCrToARGBMatrix* matrix,
const vImage_YpCbCrPixelRange* range,
const uint8_t permuteMap[4]) {
assert(width > 0 && height != 0); vImage_Buffer dstBuffer;
std::shared_ptr<ccap::Allocator> sharedAllocator = ccap::getSharedAllocator();
assert(sharedAllocator != nullptr);
auto realHeight = std::abs(height);
if (dstStride * realHeight > sharedAllocator->size()) {
sharedAllocator->resize(dstStride * realHeight);
}
if (height < 0) {
dstBuffer = { sharedAllocator->data(), (uint32_t)realHeight, (uint32_t)width, (size_t)dstStride };
} else {
dstBuffer = { dst, (uint32_t)realHeight, (uint32_t)width, (size_t)dstStride };
}
vImage_Buffer yBuffer = { (void*)srcY, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)srcYStride };
vImage_Buffer uvBuffer = { (void*)srcUV, (vImagePixelCount)(realHeight / 2), (vImagePixelCount)(width / 2), (size_t)srcUVStride };
vImage_YpCbCrToARGB info;
vImage_Error err = vImageConvert_YpCbCrToARGB_GenerateConversion(matrix, range, &info,
kvImage420Yp8_CbCr8, kvImageARGB8888, kvImageNoFlags);
if (err != kvImageNoError) {
CCAP_LOG_E("vImageConvert_YpCbCrToARGB_GenerateConversion failed: %zu", err);
return;
}
err = vImageConvert_420Yp8_CbCr8ToARGB8888(&yBuffer, &uvBuffer, &dstBuffer, &info,
permuteMap, 255, kvImageNoFlags);
if (err != kvImageNoError) {
CCAP_LOG_E("vImageConvert_420Yp8_CbCr8ToARGB8888 failed: %zu", err);
return;
}
if (height < 0) { verticalFlip_apple((const uint8_t*)dstBuffer.data, dstStride, dst, dstStride, realHeight);
}
}
void nv12ToRgbColor_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height,
ConvertFlag flag, ccap::PixelFormat targetPixelFormat) {
constexpr vImage_YpCbCrPixelRange videoRange = { 16, 128, 235, 240, 255, 0, 255, 1 };
constexpr vImage_YpCbCrPixelRange fullRange = { 0, 128, 255, 255, 255, 1, 255, 0 };
const auto* range = (flag & ConvertFlag::FullRange) ? &fullRange : &videoRange;
const vImage_YpCbCrToARGBMatrix* matrix = (flag & ConvertFlag::BT601) ? kvImage_YpCbCrToARGBMatrix_ITU_R_601_4 : kvImage_YpCbCrToARGBMatrix_ITU_R_709_2;
auto allocator = ccap::getSharedAllocator();
auto realHeight = std::abs(height);
bool hasAlpha = ccap::pixelFormatInclude(targetPixelFormat, ccap::kPixelFormatAlphaColorBit);
auto argbStride = (4 * width + 31) & ~31;
if (!hasAlpha && allocator->size() < argbStride * realHeight) {
allocator->resize(argbStride * realHeight); }
uint8_t* argbData = hasAlpha ? dst : allocator->data();
uint8_t permuteMap[4]; if (ccap::pixelFormatInclude(targetPixelFormat, ccap::kPixelFormatBGRBit)) {
permuteMap[0] = 3; permuteMap[1] = 2; permuteMap[2] = 1; permuteMap[3] = 0; } else {
permuteMap[0] = 1; permuteMap[1] = 2; permuteMap[2] = 3; permuteMap[3] = 0; }
nv12ToArgb32_apple_imp(srcY, srcYStride, srcUV, srcUVStride, argbData, argbStride, width, height, matrix, range, permuteMap);
if (!hasAlpha) {
vImage_Buffer srcBuffer = { argbData, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)argbStride };
vImage_Buffer dstBuffer = { dst, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)dstStride };
vImageConvert_RGBA8888toRGB888(&srcBuffer, &dstBuffer, kvImageNoFlags);
}
}
void nv12ToBgra32_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
nv12ToRgbColor_apple(srcY, srcYStride, srcUV, srcUVStride,
dst, dstStride, width, height, flag, ccap::PixelFormat::BGRA32);
}
void nv12ToRgba32_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
nv12ToRgbColor_apple(srcY, srcYStride, srcUV, srcUVStride,
dst, dstStride, width, height, flag, ccap::PixelFormat::RGBA32);
}
void nv12ToBgr24_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
nv12ToRgbColor_apple(srcY, srcYStride, srcUV, srcUVStride,
dst, dstStride, width, height, flag, ccap::PixelFormat::BGR24);
}
void nv12ToRgb24_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcUV, int srcUVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
nv12ToRgbColor_apple(srcY, srcYStride, srcUV, srcUVStride,
dst, dstStride, width, height, flag, ccap::PixelFormat::RGB24);
}
void i420ToBgra32_apple_imp(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height,
const vImage_YpCbCrToARGBMatrix* matrix,
const vImage_YpCbCrPixelRange* range,
const uint8_t permuteMap[4]) {
assert(width > 0 && height != 0); vImage_Buffer dstBuffer;
std::shared_ptr<ccap::Allocator> sharedAllocator = ccap::getSharedAllocator();
assert(sharedAllocator != nullptr);
auto realHeight = std::abs(height);
if (dstStride * realHeight > sharedAllocator->size()) {
sharedAllocator->resize(dstStride * realHeight);
}
if (height < 0) {
dstBuffer = { sharedAllocator->data(), (uint32_t)realHeight, (uint32_t)width, (size_t)dstStride };
} else {
dstBuffer = { dst, (uint32_t)realHeight, (uint32_t)width, (size_t)dstStride };
}
vImage_Buffer yBuffer = { (void*)srcY, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)srcYStride };
vImage_Buffer uBuffer = { (void*)srcU, (vImagePixelCount)(realHeight / 2), (vImagePixelCount)(width / 2), (size_t)srcUStride };
vImage_Buffer vBuffer = { (void*)srcV, (vImagePixelCount)(realHeight / 2), (vImagePixelCount)(width / 2), (size_t)srcVStride };
vImage_YpCbCrToARGB info;
vImage_Error err = vImageConvert_YpCbCrToARGB_GenerateConversion(matrix, range, &info,
kvImage420Yp8_Cb8_Cr8, kvImageARGB8888, kvImageNoFlags);
if (err != kvImageNoError) {
CCAP_LOG_E("vImageConvert_YpCbCrToARGB_GenerateConversion failed: %zu", err);
return;
}
err = vImageConvert_420Yp8_Cb8_Cr8ToARGB8888(&yBuffer, &uBuffer, &vBuffer, &dstBuffer, &info, permuteMap, 255, kvImageNoFlags);
if (err != kvImageNoError) {
CCAP_LOG_E("vImageConvert_420Yp8_Cb8_Cr8ToARGB8888 failed: %zu", err);
return;
}
if (height < 0) { verticalFlip_apple((const uint8_t*)dstBuffer.data, dstStride, dst, dstStride, realHeight);
}
}
void i420ToRgbColor_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height,
ConvertFlag flag, ccap::PixelFormat targetPixelFormat) {
constexpr vImage_YpCbCrPixelRange videoRange = { 16, 128, 235, 240, 255, 0, 255, 1 }; constexpr vImage_YpCbCrPixelRange fullRange = { 0, 128, 255, 255, 255, 1, 255, 0 };
const auto* range = (flag & ConvertFlag::FullRange) ? &fullRange : &videoRange;
const vImage_YpCbCrToARGBMatrix* matrix = (flag & ConvertFlag::BT601) ? kvImage_YpCbCrToARGBMatrix_ITU_R_601_4 : kvImage_YpCbCrToARGBMatrix_ITU_R_709_2;
auto allocator = ccap::getSharedAllocator();
auto realHeight = std::abs(height);
bool hasAlpha = ccap::pixelFormatInclude(targetPixelFormat, ccap::kPixelFormatAlphaColorBit);
auto argbStride = (4 * width + 31) & ~31;
if (!hasAlpha && allocator->size() < argbStride * realHeight) {
allocator->resize(argbStride * realHeight); }
uint8_t* argbData = hasAlpha ? dst : allocator->data();
uint8_t permuteMap[4]; if (ccap::pixelFormatInclude(targetPixelFormat, ccap::kPixelFormatBGRBit)) {
permuteMap[0] = 3; permuteMap[1] = 2; permuteMap[2] = 1; permuteMap[3] = 0; } else {
permuteMap[0] = 1; permuteMap[1] = 2; permuteMap[2] = 3; permuteMap[3] = 0; }
i420ToBgra32_apple_imp(srcY, srcYStride, srcU, srcUStride, srcV, srcVStride, argbData, argbStride, width, height, matrix, range, permuteMap);
if (!hasAlpha) {
vImage_Buffer srcBuffer = { argbData, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)argbStride };
vImage_Buffer dstBuffer = { dst, (vImagePixelCount)realHeight, (vImagePixelCount)width, (size_t)dstStride };
vImageConvert_RGBA8888toRGB888(&srcBuffer, &dstBuffer, kvImageNoFlags);
}
}
void i420ToBgra32_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
i420ToRgbColor_apple(srcY, srcYStride, srcU, srcUStride,
srcV, srcVStride, dst, dstStride,
width, height, flag, ccap::PixelFormat::BGRA32);
}
void i420ToRgba32_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
i420ToRgbColor_apple(srcY, srcYStride, srcU, srcUStride,
srcV, srcVStride, dst, dstStride,
width, height, flag, ccap::PixelFormat::RGBA32);
}
void i420ToBgr24_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
i420ToRgbColor_apple(srcY, srcYStride, srcU, srcUStride,
srcV, srcVStride, dst, dstStride,
width, height, flag, ccap::PixelFormat::BGR24);
}
void i420ToRgb24_apple(const uint8_t* srcY, int srcYStride,
const uint8_t* srcU, int srcUStride,
const uint8_t* srcV, int srcVStride,
uint8_t* dst, int dstStride,
int width, int height, ConvertFlag flag) {
i420ToRgbColor_apple(srcY, srcYStride, srcU, srcUStride,
srcV, srcVStride, dst, dstStride,
width, height, flag, ccap::PixelFormat::RGB24);
}
}
#endif