概述
在《[#跟着小白一起学鸿蒙# 七] 写个NAPI子系统》的文章里我们熟悉了如何用NAPI框架实现一个HAP应用的业务接口,但是这只是OpenHarmony提供的一种实现方式。在ETS的框架接口里OpenHarmony提供了一种更方便快捷的方式就是利用XComponent组件和NDK的方式快速进行业务层逻辑实现和动态库(so)的调用。
XComponent组件
参考文档链接:https://developer.harmonyos.com/cn/docs/documentation/doc-references/ts-basic-components-xcomponent-0000001333800561
接口
XComponent(value: {id: string, type: string, libraryname?: string, controller?: XComponentController})
参数:
参数名 | 参数类型 | 必填 | 描述 |
id | string | 是 | 组件的唯一标识,支持最大的字符串长度128。 |
type | string | 是 | 用于指定XComponent组件类型,可选值为: -surface:组件内容单独送显,直接合成到屏幕。 -component:组件内容与其他组件合成后统一送显。 |
libraryname | string | 否 | 应用Native层编译输出动态库名称。 |
controller | 否 | 给组件绑定一个控制器,通过控制器调用组件方法。 |
事件
插件加载完毕后的回调事件(onLoad):onLoad(callback: (event?: object) => void )
参数:
参数名 | 参数类型 | 必填 | 描述 |
event | object | 否 | 获取XComponent实例对象的context,context上挂载的方法由开发者在c++层定义。 |
插件卸载完毕后的回调(onDestroy):onDestroy(event: () => void )。
获取C++实例对象接口。
getXComponentContext()。
返回值:
类型 | 描述 |
Object | 获取XComponent实例对象的context,context包含的具体接口方法由开发者自定义。 |
样例开发
应用层开发:
和一般的HAP应用开发一样基于ets的页面开发,代码如下:
import nativerender from "libnativerender.so";
import { ContextType } from "../common/Constants"
const nativePageLifecycle = nativerender.getContext(ContextType.JSPAGE_LIFECYCLE);
@Entry
@Component
struct Index {
private context = null;
aboutToAppear() {
console.log('[LIFECYCLE-Index] aboutToAppear');
nativePageLifecycle.aboutToAppear();
}
aboutToDisappear() {
console.log('[LIFECYCLE-Index] aboutToDisappear');
nativePageLifecycle.aboutToDisappear();
}
onPageShow() {
console.log('[LIFECYCLE-Page] onPageShow');
nativePageLifecycle.onPageShow();
}
onPageHide() {
console.log('[LIFECYCLE-Page] onPageHide');
nativePageLifecycle.onPageHide();
}
build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
Button('ChangeColor')
.onClick(() => {
if (this.context) {
this.context.changeColor();
}
})
.width(200)
.height(80)
Button('ChangeShape')
.onClick(() => {
if (this.context) {
this.context.changeShape();
}
})
.width(200)
.height(80)
XComponent({ id: 'xcomponentId', type: 'texture', libraryname: 'nativerender'})
.onLoad((context) => {
this.context = context;
})
.onDestroy(() => {
})
}
.width('100%')
.height('100%')
}
}
CPP层开发:
编写CMakeList:
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)
project(XComponent)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-DOHOS_PLATFORM)
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/common
${NATIVERENDER_ROOT_PATH}/napi
${NATIVERENDER_ROOT_PATH}/render
)
add_library(nativerender SHARED
render/egl_core.cpp
render/plugin_render.cpp
plugin_manager.cpp
napi/napi_init.cpp
)
find_library( # Sets the name of the path variable.
EGL-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
EGL )
find_library( # Sets the name of the path variable.
GLES-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
GLESv3 )
find_library( # Sets the name of the path variable.
hilog-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
hilog_ndk.z )
find_library( # Sets the name of the path variable.
libace-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
ace_ndk.z )
find_library( # Sets the name of the path variable.
libnapi-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
ace_napi.z )
find_library( # Sets the name of the path variable.
libuv-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
uv )
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libc++.
NAPI接口:
#include "plugin_common.h"
#include "plugin_manager.h"
/*
* function for module exports
*/
static napi_value Init(napi_env env, napi_value exports)
{
LOGE("Init");
napi_property_descriptor desc[] ={
DECLARE_NAPI_FUNCTION("getContext", PluginManager::GetContext),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
bool ret = PluginManager::GetInstance()->Export(env, exports);
if (!ret) {
LOGE("Init failed");
}
return exports;
}
/*
* Napi Module define
*/
static napi_module nativerenderModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "nativerender",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
/*
* Module register function
*/
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&nativerenderModule);
}
XComponent框架适配:
#include <stdint.h>
#include <string>
#include <stdio.h>
#include <ace/xcomponent/native_interface_xcomponent.h>
#include "plugin_manager.h"
#include "plugin_common.h"
enum ContextType {
APP_LIFECYCLE = 0,
JS_PAGE_LIFECYCLE,
};
PluginManager PluginManager::manager_;
napi_value PluginManager::GetContext(napi_env env, napi_callback_info info)
{
napi_status status;
napi_value exports;
size_t argc = 1;
napi_value args[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));
if (argc != 1) {
napi_throw_type_error(env, NULL, "Wrong number of arguments");
return nullptr;
}
napi_valuetype valuetype;
status = napi_typeof(env, args[0], &valuetype);
if (status != napi_ok) {
return nullptr;
}
if (valuetype != napi_number) {
napi_throw_type_error(env, NULL, "Wrong arguments");
return nullptr;
}
int64_t value;
NAPI_CALL(env, napi_get_value_int64(env, args[0], &value));
NAPI_CALL(env, napi_create_object(env, &exports));
switch (value) {
case APP_LIFECYCLE:
{
/**** AppInit 对应 app.ets中的应用生命周期 onCreate, onShow, onHide, onDestroy ******/
LOGD("GetContext APP_LIFECYCLE");
/**** Register App Lifecycle ******/
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("onCreate", PluginManager::NapiOnCreate),
DECLARE_NAPI_FUNCTION("onShow", PluginManager::NapiOnShow),
DECLARE_NAPI_FUNCTION("onHide", PluginManager::NapiOnHide),
DECLARE_NAPI_FUNCTION("onDestroy", PluginManager::NapiOnDestroy),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
}
break;
case JS_PAGE_LIFECYCLE:
{
/**************** 声明式开发范式 JS Page 生命周期注册 ****************************/
LOGD("GetContext JS_PAGE_LIFECYCLE");
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("onPageShow", PluginManager::NapiOnPageShow),
DECLARE_NAPI_FUNCTION("onPageHide", PluginManager::NapiOnPageHide),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
}
break;
default:
LOGE("unknown type");
}
return exports;
}
bool PluginManager::Export(napi_env env, napi_value exports)
{
napi_status status;
napi_value exportInstance = nullptr;
OH_NativeXComponent *nativeXComponent = nullptr;
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { };
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
if (status != napi_ok) {
return false;
}
status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
if (status != napi_ok) {
return false;
}
ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return false;
}
std::string id(idStr);
auto context = PluginManager::GetInstance();
if (context) {
context->SetNativeXComponent(id, nativeXComponent);
auto render = context->GetRender(id);
render->SetNativeXComponent(nativeXComponent);
render->Export(env, exports);
return true;
}
return false;
}
void PluginManager::SetNativeXComponent(std::string& id, OH_NativeXComponent* nativeXComponent)
{
if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) {
nativeXComponentMap_[id] = nativeXComponent;
} else {
if (nativeXComponentMap_[id] != nativeXComponent) {
nativeXComponentMap_[id] = nativeXComponent;
}
}
}
OH_NativeXComponent* PluginManager::GetNativeXComponent(std::string& id)
{
if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) {
return nullptr;
} else {
return nativeXComponentMap_[id];
}
}
PluginRender* PluginManager::GetRender(std::string& id)
{
if (pluginRenderMap_.find(id) == pluginRenderMap_.end()) {
PluginRender* instance = PluginRender::GetInstance(id);
pluginRenderMap_[id] = instance;
return instance;
} else {
return pluginRenderMap_[id];
}
}
void PluginManager::MainOnMessage(const uv_async_t* req)
{
LOGD("MainOnMessage Triggered");
}
napi_value PluginManager::NapiOnCreate(napi_env env, napi_callback_info info)
{
LOGD("PluginManager::NapiOnCreate");
uv_loop_t* loop = nullptr;
uv_check_t* check = new uv_check_t;
NAPI_CALL(env, napi_get_uv_event_loop(env, &loop));
PluginManager::GetInstance()->OnCreateNative(env, loop);
return nullptr;
}
napi_value PluginManager::NapiOnShow(napi_env env, napi_callback_info info)
{
PluginManager::GetInstance()->OnShowNative();
return nullptr;
}
napi_value PluginManager::NapiOnHide(napi_env env, napi_callback_info info)
{
PluginManager::GetInstance()->OnHideNative();
return nullptr;
}
napi_value PluginManager::NapiOnDestroy(napi_env env, napi_callback_info info)
{
PluginManager::GetInstance()->OnDestroyNative();
return nullptr;
}
void PluginManager::OnCreateNative(napi_env env, uv_loop_t* loop)
{
mainEnv_ = env;
mainLoop_ = loop;
if (mainLoop_) {
uv_async_init(mainLoop_, &mainOnMessageSignal_, reinterpret_cast<uv_async_cb>(PluginManager::MainOnMessage));
}
}
void PluginManager::OnShowNative()
{
LOGD("PluginManager::OnShowNative");
}
void PluginManager::OnHideNative()
{
LOGD("PluginManager::OnHideNative");
}
void PluginManager::OnDestroyNative()
{
LOGD("PluginManager::OnDestroyNative");
}
napi_value PluginManager::NapiOnPageShow(napi_env env, napi_callback_info info)
{
LOGD("PluginManager::NapiOnPageShow");
return nullptr;
}
napi_value PluginManager::NapiOnPageHide(napi_env env, napi_callback_info info)
{
LOGD("PluginManager::NapiOnPageHide");
return nullptr;
}
void PluginManager::OnPageShowNative()
{
LOGD("PluginManager::OnPageShowNative");
}
void PluginManager::OnPageHideNative()
{
LOGD("PluginManager::OnPageHideNative");
}
界面渲染适配:
#include <stdint.h>
#include "plugin_render.h"
#include "plugin_common.h"
#include "plugin_manager.h"
#ifdef __cplusplus
extern "C" {
#endif
std::unordered_map<std::string, PluginRender*> PluginRender::instance_;
OH_NativeXComponent_Callback PluginRender::callback_;
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{
LOGD("OnSurfaceCreatedCB");
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return;
}
std::string id(idStr);
auto render = PluginRender::GetInstance(id);
render->OnSurfaceCreated(component, window);
}
void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window)
{
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return;
}
std::string id(idStr);
auto render = PluginRender::GetInstance(id);
render->OnSurfaceChanged(component, window);
}
void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window)
{
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return;
}
std::string id(idStr);
auto render = PluginRender::GetInstance(id);
render->OnSurfaceDestroyed(component, window);
}
void DispatchTouchEventCB(OH_NativeXComponent* component, void* window)
{
LOGD("DispatchTouchEventCB");
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return;
}
std::string id(idStr);
auto render = PluginRender::GetInstance(id);
render->DispatchTouchEvent(component, window);
}
PluginRender::PluginRender(std::string& id) : id_(id), component_(nullptr)
{
eglCore_ = new EGLCore(id);
auto renderCallback = PluginRender::GetNXComponentCallback();
renderCallback->OnSurfaceCreated = OnSurfaceCreatedCB;
renderCallback->OnSurfaceChanged = OnSurfaceChangedCB;
renderCallback->OnSurfaceDestroyed = OnSurfaceDestroyedCB;
renderCallback->DispatchTouchEvent = DispatchTouchEventCB;
}
PluginRender* PluginRender::GetInstance(std::string& id)
{
if (instance_.find(id) == instance_.end()) {
PluginRender* instance = new PluginRender(id);
instance_[id] = instance;
return instance;
} else {
return instance_[id];
}
}
OH_NativeXComponent_Callback* PluginRender::GetNXComponentCallback()
{
return &PluginRender::callback_;
}
void PluginRender::SetNativeXComponent(OH_NativeXComponent* component)
{
component_ = component;
OH_NativeXComponent_RegisterCallback(component_, &PluginRender::callback_);
}
void PluginRender::OnSurfaceCreated(OH_NativeXComponent* component, void* window)
{
LOGD("PluginRender::OnSurfaceCreated");
int32_t ret=OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
eglCore_->GLContextInit(window, width_, height_);
}
}
void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window)
{
}
void PluginRender::OnSurfaceDestroyed(OH_NativeXComponent* component, void* window)
{
}
void PluginRender::DispatchTouchEvent(OH_NativeXComponent* component, void* window)
{
int32_t ret = OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent_);
if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
LOGD("Touch Info : x = %{public}f, y = %{public}f screenx = %{public}f, screeny = %{public}f", touchEvent_.x, touchEvent_.y, touchEvent_.screenX, touchEvent_.screenY);
for (int i=0;i<touchEvent_.numPoints;i++) {
LOGE("Touch Info : dots[%{public}d] id %{public}d x = %{public}f, y = %{public}f", i, touchEvent_.touchPoints[i].id, touchEvent_.touchPoints[i].x, touchEvent_.touchPoints[i].y);
LOGE("Touch Info : screenx = %{public}f, screeny = %{public}f", touchEvent_.touchPoints[i].screenX, touchEvent_.touchPoints[i].screenY);
LOGE("vtimeStamp = %{public}llu, isPressed = %{public}d", touchEvent_.touchPoints[i].timeStamp, touchEvent_.touchPoints[i].isPressed);
}
} else {
LOGE("Touch fail");
}
}
napi_value PluginRender::Export(napi_env env, napi_value exports)
{
LOGE("PluginRender::Export");
// Register JS API
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("changeShape", PluginRender::NapiChangeShape),
DECLARE_NAPI_FUNCTION("drawTriangle", PluginRender::NapiDrawTriangle),
DECLARE_NAPI_FUNCTION("changeColor", PluginRender::NapiChangeColor),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
napi_value PluginRender::NapiChangeShape(napi_env env, napi_callback_info info)
{
LOGD("NapiChangeShape");
napi_value exportInstance;
napi_value thisArg;
napi_status status;
OH_NativeXComponent *nativeXComponent = nullptr;
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
// napi_value thisArg;
NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL));
status = napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
if (status != napi_ok) {
return nullptr;
};
status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
if (status != napi_ok) {
return nullptr;
}
ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return nullptr;
}
std::string id(idStr);
PluginRender* instance = PluginRender::GetInstance(id);
if (instance) {
instance->eglCore_->ChangeShape();
}
return nullptr;
}
napi_value PluginRender::NapiDrawTriangle(napi_env env, napi_callback_info info)
{
LOGD("NapiDrawTriangle");
napi_value exportInstance;
napi_value thisArg;
napi_status status;
OH_NativeXComponent *nativeXComponent = nullptr;
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
// napi_value thisArg;
NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL));
status = napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
if (status != napi_ok) {
return nullptr;
};
status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
if (status != napi_ok) {
return nullptr;
}
ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return nullptr;
}
std::string id(idStr);
PluginRender* instance = PluginRender::GetInstance(id);
if (instance) {
instance->eglCore_->DrawTriangle();
}
return nullptr;
}
napi_value PluginRender::NapiChangeColor(napi_env env, napi_callback_info info)
{
LOGD("NapiChangeColor");
napi_value exportInstance;
napi_value thisArg;
napi_status status;
OH_NativeXComponent *nativeXComponent = nullptr;
int32_t ret;
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
// napi_value thisArg;
NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL));
status = napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
if (status != napi_ok) {
return nullptr;
}
status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
if (status != napi_ok) {
return nullptr;
}
ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
return nullptr;
}
std::string id(idStr);
PluginRender* instance = PluginRender::GetInstance(id);
if (instance) {
instance->eglCore_->ChangeColor();
}
return nullptr;
}
#ifdef __cplusplus
}
#endif
业务逻辑适配:
#include "egl_core.h"
#include "plugin_common.h"
#include "plugin_render.h"
#include <EGL/egl.h>
#include <GLES3/gl3.h>
EGLConfig getConfig(int version, EGLDisplay eglDisplay) {
int attribList[] = {
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_ES2_BIT,
EGL_NONE
};
EGLConfig configs = NULL;
int configsNum;
if (!eglChooseConfig(eglDisplay, attribList, &configs, 1, &configsNum)) {
LOGE("eglChooseConfig ERROR");
return NULL;
}
return configs;
}
char vertexShader[] =
"#version 300 es\n"
"layout(location = 0) in vec4 a_position;\n"
"layout(location = 1) in vec4 a_color;\n"
"out vec4 v_color;\n"
"void main()\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = a_color;\n"
"}\n";
char fragmentShader[] =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 v_color;\n"
"out vec4 fragColor;\n"
"void main()\n"
"{\n"
" fragColor = v_color;\n"
"}\n";
void EGLCore::GLContextInit(void* window, int w, int h)
{
LOGD("EGLCore::GLContextInit window = %{public}p, w = %{public}d, h = %{public}d.", window, w, h);
width_ = w;
height_ = h;
mEglWindow = static_cast<EGLNativeWindowType>(window);
// 1. create sharedcontext
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL_NO_DISPLAY) {
LOGE("EGLCore::unable to get EGL display.");
return;
}
EGLint eglMajVers, eglMinVers;
if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
mEGLDisplay = EGL_NO_DISPLAY;
LOGE("EGLCore::unable to initialize display");
return;
}
mEGLConfig = getConfig(3, mEGLDisplay);
if (mEGLConfig == nullptr) {
LOGE("EGLCore::GLContextInit config ERROR");
return;
}
// 2. Create EGL Surface from Native Window
EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
if (mEglWindow) {
mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, winAttribs);
if (mEGLSurface == nullptr) {
LOGE("EGLCore::eglCreateContext eglSurface is null");
return;
}
}
// 3. Create EGLContext from
int attrib3_list[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list);
if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError());
}
mProgramHandle = CreateProgram(vertexShader, fragmentShader);
if (!mProgramHandle) {
LOGE("EGLCore::Could not create CreateProgram");
return;
}
DrawTriangle();
}
void EGLCore::DrawTriangle()
{
GLfloat color[] = {
0.5f, 0.6f, 0.3f, 1.0f
};
const GLfloat triangleVertices[] = {
0.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
};
glViewport(0, 0, width_, height_);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(mProgramHandle);
GLint positionHandle = glGetAttribLocation(mProgramHandle, "a_position");
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
glEnableVertexAttribArray(positionHandle);
glVertexAttrib4fv(1, color);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(positionHandle);
glFlush();
glFinish();
eglSwapBuffers(mEGLDisplay, mEGLSurface);
}
void EGLCore::ChangeShape()
{
if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError());
}
GLfloat color[] = {
0.7f, 0.2f, 0.2f, 1.0f
};
const GLfloat triangleVertices[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 0.0f
};
glViewport(0, 0, width_, height_);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(mProgramHandle);
GLint positionHandle = glGetAttribLocation(mProgramHandle, "a_position");
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
glEnableVertexAttribArray(positionHandle);
glVertexAttrib4fv(1, color);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(positionHandle);
Update();
}
void EGLCore::ChangeColor()
{
if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
LOGE("EGLCore::eglMakeCurrent error = %{public}d", eglGetError());
}
GLfloat color[] = {
0.9f, 0.5f, 0.7f, 1.0f
};
const GLfloat triangleVertices[] = {
0.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
};
glViewport(0, 0, width_, height_);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(mProgramHandle);
GLint positionHandle = glGetAttribLocation(mProgramHandle, "a_position");
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
glEnableVertexAttribArray(positionHandle);
glVertexAttrib4fv(1, color);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(positionHandle);
Update();
}
void EGLCore::Update()
{
eglSwapBuffers(mEGLDisplay, mEGLSurface);
}
GLuint EGLCore::LoadShader(GLenum type, const char *shaderSrc)
{
GLuint shader;
GLint compiled;
shader = glCreateShader(type);
if (shader == 0) {
LOGE("LoadShader shader error");
return 0;
}
glShaderSource(shader, 1, &shaderSrc, nullptr);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = (char*)malloc(sizeof(char) * infoLen);
glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
LOGE("Error compiling shader:\n%s\n",infoLog);
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
GLuint EGLCore::CreateProgram(const char *vertexShader, const char *fragShader)
{
GLuint vertex;
GLuint fragment;
GLuint program;
GLint linked;
vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
if (vertex == 0) {
LOGE("CreateProgram vertex error");
return 0;
}
fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
if (fragment == 0) {
LOGE("CreateProgram fragment error");
glDeleteShader(vertex);
return 0;
}
program = glCreateProgram();
if (program == 0) {
LOGE("CreateProgram program error");
glDeleteShader(vertex);
glDeleteShader(fragment);
return 0;
}
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
LOGE("CreateProgram linked error");
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = (char *)malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
LOGE("Error linking program:\n%s\n",infoLog);
free(infoLog);
}
glDeleteShader(vertex);
glDeleteShader(fragment);
glDeleteProgram(program);
return 0;
}
glDeleteShader(vertex);
glDeleteShader(fragment);
return program;
}
bool EGLCore::checkGlError(const char* op)
{
LOGE("EGL ERROR CODE = %{public}x", eglGetError());
GLint error;
for (error = glGetError(); error; error = glGetError()) {
LOGE("ERROR: %{public}s, ERROR CODE = %{public}x", op, error);
return true;
}
return false;
}
小结
通过上述方式,我们就能编译并安装运行应用了,上述代码链接在:https://toscode.gitee.com/openharmony/app_samples/tree/master/ETSUI/XComponent。