* Copyright (c) 2026 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 "NativeImageAdaptor.h"
#include "logger_common.h"
#include "cstdint"
namespace NativeWindowSample {
using GetPlatformDisplayExt = PFNEGLGETPLATFORMDISPLAYEXTPROC;
constexpr const char *EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland";
constexpr const char *EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland";
constexpr int32_t EGL_CONTEXT_CLIENT_VERSION_NUM = 2;
constexpr char CHARACTER_WHITESPACE = ' ';
constexpr const char *CHARACTER_STRING_WHITESPACE = " ";
constexpr const char *EGL_GET_PLATFORM_DISPLAY_EXT = "eglGetPlatformDisplayEXT";
constexpr int32_t NATIVE_CACHE_BUFFER = 3;
constexpr int32_t GSERROR_OK = 0;
constexpr int32_t GSERROR_FAILD = 1;
NativeImageAdaptor *NativeImageAdaptor::GetInstance()
{
static NativeImageAdaptor imageAdaptor;
return &imageAdaptor;
};
bool NativeImageAdaptor::CheckEglExtension(const char *eglExtensions, const char *eglExtension)
{
size_t extLenth = strlen(eglExtension);
const char *endPos = eglExtensions + strlen(eglExtensions);
while (eglExtensions < endPos) {
size_t len = 0;
if (*eglExtensions == CHARACTER_WHITESPACE) {
eglExtensions++;
continue;
}
len = strcspn(eglExtensions, CHARACTER_STRING_WHITESPACE);
if (len == extLenth && strncmp(eglExtension, eglExtensions, len) == 0) {
return true;
}
eglExtensions += len;
}
return false;
}
EGLDisplay NativeImageAdaptor::GetPlatformEglDisplay(EGLenum platform, void *native_display, const EGLint *attrib_list)
{
static GetPlatformDisplayExt eglGetPlatformDisplayExt = NULL;
if (!eglGetPlatformDisplayExt) {
const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (extensions && (CheckEglExtension(extensions, EGL_EXT_PLATFORM_WAYLAND) ||
CheckEglExtension(extensions, EGL_KHR_PLATFORM_WAYLAND))) {
eglGetPlatformDisplayExt = (GetPlatformDisplayExt)eglGetProcAddress(EGL_GET_PLATFORM_DISPLAY_EXT);
}
}
if (eglGetPlatformDisplayExt) {
return eglGetPlatformDisplayExt(platform, native_display, attrib_list);
}
return eglGetDisplay((EGLNativeDisplayType)native_display);
}
void NativeImageAdaptor::InitEGLEnv()
{
LOGD("NativeImageAdaptor::InitEGLEnv begin");
eglDisplay_ = GetPlatformEglDisplay(EGL_PLATFORM_OHOS_KHR, EGL_DEFAULT_DISPLAY, NULL);
if (eglDisplay_ == EGL_NO_DISPLAY) {
LOGE("NativeImageAdaptor::InitEGLEnv fail");
}
EGLint major, minor;
if (eglInitialize(eglDisplay_, &major, &minor) == EGL_FALSE) {
LOGE("NativeImageAdaptor::eglInitialize fail");
}
if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
LOGE("eglBindAPI fail");
}
unsigned int ret;
EGLint count;
EGLint config_attribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE,
8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE};
ret = eglChooseConfig(eglDisplay_, config_attribs, &config_, 1, &count);
if (!(ret && static_cast<unsigned int>(count) >= 1)) {
LOGE("eglChooseConfig fail");
}
static const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE};
eglContext_ = eglCreateContext(eglDisplay_, config_, EGL_NO_CONTEXT, context_attribs);
if (eglContext_ == EGL_NO_CONTEXT) {
LOGE("eglCreateContext fail");
}
eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext_);
LOGD("Create EGL context successfully");
}
bool NativeImageAdaptor::Export(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{"GetAvailableCount", nullptr, NativeImageAdaptor::GetAvailableCount, nullptr, nullptr, nullptr, napi_default,
nullptr},
{"GetBufferQueueSize", nullptr, NativeImageAdaptor::NapiOnGetBufferQueueSize, nullptr, nullptr, nullptr,
napi_default, nullptr},
{"GetAttachBufferCount", nullptr, NativeImageAdaptor::NapiOnGetAttachBufferCount, nullptr, nullptr, nullptr,
napi_default, nullptr},
{"GetCacheBufferCount", nullptr, NativeImageAdaptor::NapiOnGetCacheBufferCount, nullptr, nullptr, nullptr,
napi_default, nullptr},
{"ProduceBuffer", nullptr, NativeImageAdaptor::NapiOnProduceBuffer, nullptr, nullptr, nullptr, napi_default,
nullptr},
{"ConsumerBuffer", nullptr, NativeImageAdaptor::NapiOnConsumerBuffer, nullptr, nullptr, nullptr, napi_default,
nullptr},
{"AttachBuffer", nullptr, NativeImageAdaptor::NapiOnAttachBuffer, nullptr, nullptr, nullptr, napi_default,
nullptr},
{"DettachBuffer", nullptr, NativeImageAdaptor::NapiOnDettachBuffer, nullptr, nullptr, nullptr, napi_default,
nullptr},
{"ChangeIsAutoConsumer", nullptr, NativeImageAdaptor::NapiOnChangeIsAutoConsumer, nullptr, nullptr, nullptr,
napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
eglContext_ = EGL_NO_CONTEXT;
eglDisplay_ = EGL_NO_DISPLAY;
availableBufferCount_ = 0;
InitEGLEnv();
bool ret = InitNativeWindow();
ret = InitNativeWindowCache() && ret;
return ret;
}
bool NativeImageAdaptor::InitNativeWindow()
{
width_ = 0x100;
height_ = 0x100;
GLuint textureId;
glGenTextures(1, &textureId);
image_ = OH_NativeImage_Create(textureId, GL_TEXTURE_2D);
int32_t ret = OH_ConsumerSurface_SetDefaultSize(image_, width_, height_);
if (ret != 0) {
LOGE("NativeImageAdaptor OH_ConsumerSurface_SetDefaultSize fail");
}
uint64_t defaultUsage = NATIVEBUFFER_USAGE_CPU_READ |
NATIVEBUFFER_USAGE_CPU_WRITE |
NATIVEBUFFER_USAGE_MEM_DMA |
1ULL<<50;
ret = OH_ConsumerSurface_SetDefaultUsage(image_, defaultUsage);
if (ret != 0) {
LOGE("NativeImageAdaptor OH_ConsumerSurface_SetDefaultUsage fail");
}
nativeWindow_ = OH_NativeImage_AcquireNativeWindow(image_);
if (nativeWindow_ == nullptr) {
LOGE("NativeImageAdaptor OH_NativeImage_AcquireNativeWindow fail");
}
OH_OnFrameAvailableListener listener;
listener.context = static_cast<void*>(image_);
listener.onFrameAvailable = NativeImageAdaptor::OnFrameAvailable;
ret = OH_NativeImage_SetOnFrameAvailableListener(image_, listener);
uint64_t surfaceId;
ret = OH_NativeImage_GetSurfaceId(image_, &surfaceId);
if (ret != 0) {
LOGE("OH_NativeImage_GetSurfaceId fail");
return false;
}
OHNativeWindow *nativeWindow = nullptr;
ret = OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
if (ret != 0 || nativeWindow != nativeWindow_) {
LOGE("OH_NativeWindow_CreateNativeWindowFromSurfaceId fail");
return false;
}
uint64_t surfaceIdTmp = 0;
ret = OH_NativeWindow_GetSurfaceId(nativeWindow, &surfaceIdTmp);
if (ret != 0 || surfaceIdTmp != surfaceId) {
LOGE("OH_NativeWindow_GetSurfaceId fail");
}
OH_NativeWindow_DestroyNativeWindow(nativeWindow);
return true;
}
bool NativeImageAdaptor::InitNativeWindowCache()
{
GLuint textureId;
glGenTextures(1, &textureId);
imageCache_ = OH_NativeImage_Create(textureId, GL_TEXTURE_2D);
nativeWindowCache_ = OH_NativeImage_AcquireNativeWindow(imageCache_);
int code = SET_BUFFER_GEOMETRY;
width_ = 0x100;
height_ = 0x100;
int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindowCache_, code, width_, height_);
if (ret != 0) {
LOGE("NativeImageAdaptor OH_NativeWindow_NativeWindowHandleOpt fail");
}
code = SET_USAGE;
int32_t usage = NATIVEBUFFER_USAGE_CPU_READ | NATIVEBUFFER_USAGE_CPU_WRITE | NATIVEBUFFER_USAGE_MEM_DMA;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindowCache_, code, usage);
for (int i = 0; i < NATIVE_CACHE_BUFFER; i++) {
ProduceBuffer(0x00, nativeWindowCache_);
}
return true;
}
napi_value NativeImageAdaptor::NapiOnAttachBuffer(napi_env env, napi_callback_info info)
{
NativeImageAdaptor::GetInstance()->AttachBuffer();
return nullptr;
}
napi_value NativeImageAdaptor::NapiOnDettachBuffer(napi_env env, napi_callback_info info)
{
NativeImageAdaptor::GetInstance()->DettachBuffer();
return nullptr;
}
void NativeImageAdaptor::AttachBuffer()
{
if (bufferCache_.size() == 0) {
LOGE("bufferCache_ empty");
return;
}
NativeWindowBuffer* buffer = bufferCache_.front();
int ret = OH_NativeWindow_NativeWindowAttachBuffer(nativeWindowCache_, buffer);
if (ret != 0) {
LOGE("OH_NativeWindow_NativeWindowAttachBuffer fail");
return;
}
bufferAttached_.push(buffer);
bufferCache_.pop();
}
void NativeImageAdaptor::DettachBuffer()
{
if (bufferAttached_.size() == 0) {
LOGE("bufferAttached_ empty");
return;
}
NativeWindowBuffer *buffer = bufferAttached_.front();
int ret = OH_NativeWindow_NativeWindowDetachBuffer(nativeWindowCache_, buffer);
if (ret != 0) {
LOGE("OH_NativeWindow_NativeWindowDetachBuffer fail");
return;
}
bufferCache_.push(buffer);
bufferAttached_.pop();
}
void NativeImageAdaptor::SetConfigAndGetValue()
{
static int32_t gCnt = 0;
int32_t code = SET_FORMAT;
int32_t value = NATIVEBUFFER_PIXEL_FMT_CLUT1;
int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, value);
if (ret != 0) {
LOGE("SetConfigAndGetValue SET_FORMAT fail");
}
value = 0;
code = GET_FORMAT;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, &value);
if (ret != 0 || value != NATIVEBUFFER_PIXEL_FMT_CLUT1) {
LOGE("SetConfigAndGetValue GET_FORMAT fail");
}
code = SET_TRANSFORM;
value = NATIVEBUFFER_ROTATE_180;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, value);
if (ret != 0) {
LOGE("SetConfigAndGetValue SET_TRANSFORM fail");
}
code = GET_TRANSFORM;
value = 0;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, &value);
if (ret != 0 || value != NATIVEBUFFER_ROTATE_180) {
LOGE("SetConfigAndGetValue GET_TRANSFORM fail");
}
code = SET_COLOR_GAMUT;
value = NATIVEBUFFER_COLOR_GAMUT_STANDARD_BT709;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, value);
if (ret != 0) {
LOGE("SetConfigAndGetValue SET_COLOR_GAMUT fail");
}
code = GET_COLOR_GAMUT;
value = 0;
ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, &value);
if (ret != 0 || value != NATIVEBUFFER_COLOR_GAMUT_STANDARD_BT709) {
LOGE("SetConfigAndGetValue GET_COLOR_GAMUT fail");
}
code = SET_FORMAT;
if (gCnt % 2 == 0) {
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, NATIVEBUFFER_PIXEL_FMT_RGBA_8888);
} else {
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, NATIVEBUFFER_PIXEL_FMT_YCBCR_420_SP);
}
code = SET_TRANSFORM;
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, NATIVEBUFFER_ROTATE_NONE);
code = SET_COLOR_GAMUT;
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, NATIVEBUFFER_COLOR_GAMUT_SRGB);
gCnt++;
}
void NativeImageAdaptor::GetBufferMapPlanes(NativeWindowBuffer *buffer)
{
void *virAddr = nullptr;
OH_NativeBuffer_Planes outPlanes;
OH_NativeBuffer *nativeBuffer = nullptr;
int32_t ret = OH_NativeBuffer_FromNativeWindowBuffer(buffer, &nativeBuffer);
if (ret != 0) {
LOGE("OH_NativeBuffer_FromNativeWindowBuffer fail");
return;
}
ret = OH_NativeBuffer_MapPlanes(nativeBuffer, &virAddr, &outPlanes);
if (ret != 0) {
LOGE("OH_NativeBuffer_MapPlanes fail");
return;
}
LOGD("Get planeCount: %{public}d", outPlanes.planeCount);
for (int32_t i = 0; i < outPlanes.planeCount; i++) {
LOGD("Get offset: %{public}lu rowStride: %{public}d columnStride: %{public}d", outPlanes.planes[i].offset,
outPlanes.planes[i].rowStride, outPlanes.planes[i].columnStride);
}
}
int32_t NativeImageAdaptor::ProduceBuffer(uint32_t value, OHNativeWindow *InNativeWindow)
{
if (InNativeWindow == nativeWindow_) {
SetConfigAndGetValue();
}
NativeWindowBuffer *buffer = nullptr;
int fenceFd = -1;
int ret = OH_NativeWindow_NativeWindowRequestBuffer(InNativeWindow, &buffer, &fenceFd);
if (ret != 0) {
LOGE("OH_NativeWindow_NativeWindowRequestBuffer fail");
return GSERROR_FAILD;
}
GetBufferMapPlanes(buffer);
if (InNativeWindow == nativeWindowCache_) {
OH_NativeWindow_NativeWindowDetachBuffer(nativeWindowCache_, buffer);
bufferCache_.push(buffer);
return GSERROR_OK;
}
int32_t code = GET_FORMAT;
int32_t formatType = NATIVEBUFFER_PIXEL_FMT_CLUT1;
OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, &formatType);
BufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
void *mappedAddr = mmap(handle->virAddr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);
if (formatType == NATIVEBUFFER_PIXEL_FMT_RGBA_8888) {
uint32_t *pixel = static_cast<uint32_t *>(mappedAddr);
for (uint32_t x = 0; x < width_; x++) {
for (uint32_t y = 0; y < height_; y++) {
*pixel++ = value;
}
}
}
int result = munmap(mappedAddr, handle->size);
if (result == -1) {
LOGE("munmap fail");
}
struct Region *region = new Region();
struct Region::Rect *rect = new Region::Rect();
rect->x = 0x100;
rect->y = 0x100;
rect->w = 0x100;
rect->h = 0x100;
region->rects = rect;
ret = OH_NativeWindow_NativeWindowFlushBuffer(InNativeWindow, buffer, fenceFd, *region);
if (ret != 0) {
LOGE("OH_NativeWindow_NativeWindowFlushBuffer fail");
return GSERROR_FAILD;
}
delete region;
return GSERROR_OK;
}
int32_t NativeImageAdaptor::ConsumerBuffer(uint32_t value, OHNativeWindow *InNativeWindow)
{
std::lock_guard<std::mutex> lockGuard(opMutex_);
NativeWindowBuffer *buffer = nullptr;
int fenceFd = -1;
int ret = OH_NativeImage_AcquireNativeWindowBuffer(image_, &buffer, &fenceFd);
if (ret != 0) {
LOGE("OH_NativeImage_AcquireNativeWindowBuffer fail, ret:%{public}d", ret);
return GSERROR_FAILD;
}
ret = OH_NativeImage_ReleaseNativeWindowBuffer(image_, buffer, fenceFd);
if (ret != 0) {
LOGE("OH_NativeImage_ReleaseNativeWindowBuffer fail, ret:%{public}d", ret);
return GSERROR_FAILD;
}
availableBufferCount_--;
return GSERROR_OK;
}
void NativeImageAdaptor::OnFrameAvailable(void *context)
{
NativeImageAdaptor::GetInstance()->DealCallback(context);
return;
}
void NativeImageAdaptor::DealCallback(void *context)
{
std::lock_guard<std::mutex> lockGuard(opMutex_);
LOGD("NativeImageAdaptor success OnFrameAvailable, %{public}d", availableBufferCount_);
if (isAutoConsumer_) {
int32_t ret = OH_NativeImage_UpdateSurfaceImage(image_);
if (ret != 0) {
LOGE("OH_NativeImage_UpdateSurfaceImage fail");
}
} else {
availableBufferCount_++;
}
return;
}
int32_t NativeImageAdaptor::GetCount()
{
std::lock_guard<std::mutex> lockGuard(opMutex_);
return availableBufferCount_;
}
int32_t NativeImageAdaptor::GetAttachBufferCount()
{
return bufferAttached_.size();
}
int32_t NativeImageAdaptor::GetBufferQueueSize()
{
int code = GET_BUFFERQUEUE_SIZE;
int bufferQueueSize = 0;
int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, &bufferQueueSize);
LOGD("NativeImageAdaptor success GetAttachBufferCount, %{public}d", bufferQueueSize);
return bufferQueueSize;
}
int32_t NativeImageAdaptor::GetCacheBufferCount()
{
return bufferCache_.size();
}
bool NativeImageAdaptor::ChangeIsAutoConsumer()
{
std::lock_guard<std::mutex> lockGuard(opMutex_);
isAutoConsumer_ = !isAutoConsumer_;
return isAutoConsumer_;
}
napi_value NativeImageAdaptor::GetAvailableCount(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t count = NativeImageAdaptor::GetInstance()->GetCount();
(void)napi_create_int32(env, count, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnGetBufferQueueSize(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t count = NativeImageAdaptor::GetInstance()->GetBufferQueueSize();
(void)napi_create_int32(env, count, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnGetAttachBufferCount(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t count = NativeImageAdaptor::GetInstance()->GetAttachBufferCount();
(void)napi_create_int32(env, count, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnGetCacheBufferCount(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t count = NativeImageAdaptor::GetInstance()->GetCacheBufferCount();
(void)napi_create_int32(env, count, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnChangeIsAutoConsumer(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
bool isAutoConsumer = NativeImageAdaptor::GetInstance()->ChangeIsAutoConsumer();
(void)napi_get_boolean(env, isAutoConsumer, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnProduceBuffer(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t ret = NativeImageAdaptor::GetInstance()->
ProduceBuffer(0x00, NativeImageAdaptor::GetInstance()->nativeWindow_);
(void)napi_create_int32(env, ret, &val);
return val;
}
napi_value NativeImageAdaptor::NapiOnConsumerBuffer(napi_env env, napi_callback_info info)
{
napi_value val = nullptr;
int32_t ret = NativeImageAdaptor::GetInstance()->
ConsumerBuffer(0x00, NativeImageAdaptor::GetInstance()->nativeWindow_);
(void)napi_create_int32(env, ret, &val);
return val;
}
NativeImageAdaptor::~NativeImageAdaptor()
{
OH_NativeImage_UnsetOnFrameAvailableListener(image_);
OH_NativeWindow_DestroyNativeWindow(nativeWindow_);
OH_NativeImage_Destroy(&image_);
}
}