OpenHarmony开发者论坛
标题:
OpenHarmony应用开发引入开源C/C++库---之EGL Render
[打印本页]
作者:
深开鸿_王石
时间:
2024-4-16 18:00
标题:
OpenHarmony应用开发引入开源C/C++库---之EGL Render
[md]### 前言
[napi\_generator: NAPI框架生成工具 - Gitee.com](
https://gitee.com/openharmony/na ... mples/napitutorials
)
**NAPI仓旨在为应用开发者提供更多的NativeC++接口和使用C/C++库开发的应用方式,我们这个系列会逐步介绍EGL,FFMPEG,OpenCV等图形图像处理的能力和应用开发。如果有其他三方库的需求和应用呈现也可以在仓里提issue留言。**
### XComponent
##### 定义
**Native XComponent是XComponent组件提供在Native层的实例,可作为JS层和Native层XComponent绑定的桥梁。XComponent所提供的的NDK接口都依赖于该实例。接口能力包括获取Native Window实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下:**
* **利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。**
* **在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。**
* **利用Native Window和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列**
##### 接口
**XComponent(value: {id: string, type: string, libraryname?: string, controller?: XComponentController})**
**参数:**
| **参数名** | **参数类型** | **必填** | **描述** |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **id** | **string** | **是** | **组件的唯一标识,支持最大的字符串长度128。** |
| **type** | **string** | **是** | **用于指定XComponent组件类型,可选值仅有两个为: -"surface":用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。 -"component"9+ :XComponent将变成一个容器组件,并可在其中执行非UI逻辑以动态加载显示内容。 其他值均会被视为"surface"类型** |
| **libraryname** | **string** | **否** | **应用Native层编译输出动态库名称,仅XComponent类型为"surface"时有效。** |
| **controller** | [XComponentcontroller](
https://gitee.com/openharmony/do ... componentcontroller
) | **否** | **给组件绑定一个控制器,通过控制器调用组件方法,仅XComponent类型为"surface"时有效。** |
##### XComponent10+
**XComponent(value: {id: string, type: XComponentType, libraryname?: string, controller?: XComponentController})**
**参数:**
| **参数名** | **参数类型** | **必填** | **描述** |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------- |
| **id** | **string** | **是** | **组件的唯一标识,支持最大的字符串长度128。** |
| **type** | [XComponentType](
https://gitee.com/openharmony/do ... E%E8%AF%B4%E6%98%8E
) | **是** | **用于指定XComponent组件类型。** |
| **libraryname** | **string** | **否** | **用Native层编译输出动态库名称,仅类型为SURFACE或TEXTURE时有效。** |
| **controller** | [XComponentcontroller](
https://gitee.com/openharmony/do ... componentcontroller
) | **否** | **给组件绑定一个控制器,通过控制器调用组件方法,仅类型为SURFACE或TEXTURE时有效。** |
##### XComponentType10+枚举说明
| **名称** | **描述** |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| **SURFACE** | **用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。** |
| **COMPONENT** | **XComponent将变成一个容器组件,并可在其中执行非UI逻辑以动态加载显示内容。** |
| **TEXTURE** | **用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容会和XComponent组件的内容合成后展示到屏幕上。** |
> **说明:**
>
> **type为COMPONENT("component")时,XComponent作为容器,子组件沿垂直方向布局:**
>
> * **垂直方向上对齐格式:**[FlexAlign](
https://gitee.com/openharmony/do ... -enums.md#flexalign
).Start
> * **水平方向上对齐格式:**[FlexAlign](
https://gitee.com/openharmony/do ... -enums.md#flexalign
).Center
>
> **所有的事件响应均不支持。**
>
> **布局方式更改和事件响应均可通过挂载子组件来设置。**
>
> **内部所写的非UI逻辑需要封装在一个或多个函数内。**
### 开发步骤
1. **界面开发,ArkTS侧语法介绍**
```
XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' })
.onLoad((context) => {})
.onDestroy(() => {})
```
* **id : 与XComponent组件为一一对应关系,不可重复。通常开发者可以在native侧通过OH\_NativeXComponent\_GetXComponentId接口来获取对应的id从而绑定对应的XComponent。**
* **libraryname:加载模块的名称,必须与在native侧Napi模块注册时nm\_modname的名字一致。**
> **说明:**
>
> **应用加载模块实现跨语言调用有两种方式:**
>
> 1. **使用NAPI的import方式加载:**
> ```
> import nativerender from "libnativerender.so"
> ```
> 2. **使用XComponent组件加载,本质也是使用了NAPI机制来加载。 该加载方式和import加载方式的区别在于,在加载动态库时会将XComponent的NativeXComponent实例暴露到应用的native层中,从而让开发者可以使用XComponent的NDK接口。**
>
* **onLoad事件**
* **触发时刻:XComponent准备好surface后触发。**
* **参数context:其上面挂载了暴露在模块上的native方法,使用方法类似于利用 import context2 from "libnativerender.so" 直接加载模块后获得的context2实例。**
* **时序:XComponent组件Surface可用;native层OnSurfaceCreated触发;OnSurfaceCreate回调执行完成;js层onLoad触发;**
* **onDestroy事件**
**触发时刻:XComponent组件被销毁时触发与一般ArkUI的组件销毁时机一致。**
**时序:natvice层OnSurfaceDestroyed触发;OnSurfaceDestroyed回调执行完成;js层onDestroy触发;XComponent组件Surface销毁;**
---
**xcomponent两种用法:**
1. **通过绑定XComponentController获得对应XComponent的surfaceId(该id可以唯一确定一个surface),从而传给相应的部件(如 **[AVPlayer](
https://gitee.com/openharmony/do ... -media.md#avplayer9
)、[Camera](
https://gitee.com/openharmony/do ... t/js-apis-camera.md
) )接口进行渲染。如:
```
class suf{
surfaceId:string = "";
mXComponentController: XComponentController = new XComponentController();
set(){
this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
}
}
@State surfaceId:string = "";
mXComponentController: object = new XComponentController();
XComponent({ id: '', type: 'surface', controller: this.mXComponentController })
.onLoad(() => {
let sufset = new suf()
sufset.set()
})
```
2. **通过初始化时传入native lib,然后调用ndk方法进行渲染,本章主讲这条路。**
```
// JS侧的接口声明
export default interface XComponentContext {
drawPattern(): void;
getStatus(): XComponentContextStatus;
};
type XComponentContextStatus = {
hasDraw: boolean,
hasChangeColor: boolean,
};
// ets 里的组件构建和使用
Column({ space: 10 }) {
XComponent({
id: 'xcomponentId',
type: XComponentType.SURFACE,
libraryname: 'nativerender'
})
.onLoad((xComponentContext) => {
this.xComponentContext = xComponentContext as XComponentContext;
this.currentStatus = "index";
})
.onDestroy(() => {
console.log('onDestroy');
})
Text(this.currentStatus)
.fontSize('24fp')
.fontWeight(500)
}
.onClick(() => {
let hasChangeColor: boolean = false;
if (this.xComponentContext && this.xComponentContext.getStatus()) {
hasChangeColor = this.xComponentContext.getStatus().hasChangeColor;
}
if (hasChangeColor) {
this.currentStatus = "change color";
}
})
Row() {
Button('Draw BMP')
.onClick(() => {
if (this.xComponentContext) {
this.xComponentContext.drawPattern();
let hasDraw: boolean = false;
if (this.xComponentContext.getStatus()) {
hasDraw = this.xComponentContext.getStatus().hasDraw;
}
if (hasDraw) {
this.currentStatus = "draw bmp";
}
}
})
}
```
---
2. **Native渲染**
1. **CMakeList**
```
# 编译目标so,SHARED表示动态库
add_library(nativerender SHARED
xxx.cpp
)
# 查找相关库 (包括OpenGL ES相关库和XComponent提供的ndk接口)
find_library( EGL-lib
EGL )
find_library( GLES-lib
GLESv3 )
find_library( libace-lib
ace_ndk.z )
# 编译so所需要的依赖
target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${libace-lib} libace_napi.z.so libc++.a)
```
2. **Napi模块注册**
```
// 在so被xcomponent在js层加载的时候会调用init方法进行初始化,本质就是传递nativeXComponent对象进CPP层
static napi_value Init(napi_env env, napi_value exports)
{
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
if ((env == nullptr) || (exports == nullptr)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
return nullptr;
}
napi_property_descriptor desc[] = {
{ "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }
};
if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
return nullptr;
}
PluginManager::GetInstance()->Export(env, exports);
return exports;
}
```
3. **获得NativeXComponent实例**
```
// Init方法最后调用实例化方法
void PluginManager::Export(napi_env env, napi_value exports)
{
if ((env == nullptr) || (exports == nullptr)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginManager", "Export: env or exports is null");
return;
}
// 从js对象里取OH_NATIVE_XCOMPONENT_OBJ对象
napi_value exportInstance = nullptr;
if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginManager", "Export: napi_get_named_property fail");
return;
}
// 解封装拿到NDK指针
OH_NativeXComponent* nativeXComponent = nullptr;
if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginManager", "Export: napi_unwrap fail");
return;
}
// 通过NDK 接口获取对应 JS层的 ID,此ID其实就是在JS层初始化时的 XComponent 的属性 id = "xcomponentId";
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginManager", "Export: OH_NativeXComponent_GetXComponentId fail");
return;
}
// 通过XComponent的ID来绑定JS层的组件和Native层的render;此处context和render都是native层的全局变量,通过id绑定和回调注册等待组件初始化完成并获得想要的window指针
std::string id(idStr);
auto context = PluginManager::GetInstance();
if ((context != nullptr) && (nativeXComponent != nullptr)) {
context->SetNativeXComponent(id, nativeXComponent);
auto render = context->GetRender(id);
if (render != nullptr) {
// 注册回调,收听NDK的回调消息
render->RegisterCallback(nativeXComponent);
// 注册render的对外暴露方法,JS层可以访问
render->Export(env, exports);
}
}
}
```
4. **注册XComponent回调,具体内容看 **[native xcomponent](
https://docs.openharmony.cn/page ... tive_x_component.md
)
```
// 此处需要根据NDK文档进行自学
void PluginRender::RegisterCallback(OH_NativeXComponent* nativeXComponent)
{
renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_);
mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &mouseCallback_);
OH_NativeXComponent_RegisterFocusEventCallback(nativeXComponent, OnFocusEventCB);
OH_NativeXComponent_RegisterKeyEventCallback(nativeXComponent, OnKeyEventCB);
OH_NativeXComponent_RegisterBlurEventCallback(nativeXComponent, OnBlurEventCB);
}
```
5. **创建EGL/OpenGLES**
```
// so 加载的时候初始化 EGL对象
PluginRender:
luginRender(std::string& id)
{
this->id_ = id;
this->eglCore_ = new EGLCore();
}
// xcomponent加载完毕,创建 surface 开始初始化EGL
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB");
if ((component == nullptr) || (window == nullptr)) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: component or window is null");
return;
}
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: Unable to get XComponent id");
return;
}
std::string id(idStr);
auto render = PluginRender::GetInstance(id);
uint64_t width;
uint64_t height;
int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
if (render->eglCore_->EglContextInit(window, width, height)) {
render->eglCore_->Background();
}
}
}
// EGL 初始化窗口
bool EGLCore::EglContextInit(void* window, int width, int height)
{
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute");
if ((window == nullptr) || (width <= 0) || (height <= 0)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit: param error");
return false;
}
UpdateSize(width, height);
eglWindow_ = static_cast<EGLNativeWindowType>(window);
// Init display.
eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (eglDisplay_ == EGL_NO_DISPLAY) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
return false;
}
EGLint majorVersion;
EGLint minorVersion;
if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display");
return false;
}
// Select configuration.
const EGLint maxConfigSize = 1;
EGLint numConfigs;
if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
return false;
}
return CreateEnvironment();
}
// 创建 EGL 环境创建,eglsurface 和 eglcontext 是渲染的界面和渲染上下文
bool EGLCore::CreateEnvironment()
{
// Create surface.
if (eglWindow_ == nullptr) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null");
return false;
}
eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
if (eglSurface_ == nullptr) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
eglTerminate(eglDisplay_);
return false;
}
// Create context.
eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
if (eglContext_ == nullptr) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
eglDestroySurface(eglDisplay_, eglSurface_);
eglTerminate(eglDisplay_);
return false;
}
if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
eglDestroyContext(eglDisplay_, eglContext_);
eglDestroySurface(eglDisplay_, eglSurface_);
eglTerminate(eglDisplay_);
return false;
}
// Create program,创建纹理和对应的着色器程序.
program_ = TRCreateProgram(EGLTR_VERTEX_SHADER, EGLTR_FRAGMENT_SHADER);
// program_ = CreateProgram(EGLDRAW_VERTEX_SHADER, EGLDRAW_FRAGMENT_SHADER);
if (program_ == PROGRAM_ERROR) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
return false;
}
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: success!");
return true;
}
```
6. **进行绘制**
```
// js层
this.xComponentContext.drawPattern();
// 接口注册
{"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
// 接口实现
napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info)
{
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern");
if ((env == nullptr) || (info == nullptr)) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern: env or info is null");
return nullptr;
}
// 取传参
napi_value thisArg;
if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern: napi_get_cb_info fail");
return nullptr;
}
// 从传参里取js层xcomponent对象
napi_value exportInstance;
if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern: napi_get_named_property fail");
return nullptr;
}
// 解封装获得native层的xcomponent对象
OH_NativeXComponent* nativeXComponent = nullptr;
if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern: napi_unwrap fail");
return nullptr;
}
// 获取xcomponent的id
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
OH_LOG_Print(
LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "
luginRender", "NapiDrawPattern: Unable to get XComponent id");
return nullptr;
}
// 根据id找render,然后就可以拿eglcore画图
std::string id(idStr);
PluginRender* render = PluginRender::GetInstance(id);
if (render != nullptr) {
render->eglCore_->DrawBmp(hasDraw_);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed");
}
return nullptr;
}
```
7. **EGL 方法**
```
void EGLCore:
rawBmp(uint32_t fd, uint32_t foff, uint32_t flen)
{
flag_ = false;
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Drawbmp");
FILE *file = fdopen(fd, "r");
if (file == NULL) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ResfdExecuteCB fdopen failed!");
return;
}
if (fseek(file, foff, SEEK_SET) != 0) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ResfdExecuteCB fseek failed!");
return;
}
unsigned char *mediaData = new unsigned char[flen];
if (mediaData == NULL) {
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "new buffer failed!");
fclose(file);
return;
}
size_t readCnt = fread(mediaData, sizeof(unsigned char), flen, file);
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "readcnt: %{public}zu", readCnt);
int width = 0;
int height = 0;
unsigned char *pdata = mediaData + NUM_18;
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "width: %{public}d", *((uint32_t *)pdata));
width = *((uint32_t *)pdata);
pdata = mediaData + NUM_18 + NUM_4;
OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "height: %{public}d", *((uint32_t *)pdata));
height = *((uint32_t *)pdata);
float vertices[] = {
// positions // colors // texture coords
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // top right
1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, -1.0f, // bottom right
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f, // bottom left
-1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 2.0f // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO = 0;
unsigned int VAO = 0;
unsigned int EBO = 0;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, NUM_3, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, NUM_3, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)(NUM_3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(NUM_2, NUM_2, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)(NUM_6 * sizeof(float)));
glEnableVertexAttribArray(NUM_2);
// load and create a texture
// -------------------------
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// all upcoming GL_TEXTURE_2D operations now have effect on this texture object
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
// set texture wrapping to GL_CLAMP_TO_EDGE (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture and generate mipmaps
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, mediaData);
glGenerateMipmap(GL_TEXTURE_2D);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(program_);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, NUM_6, GL_UNSIGNED_INT, 0);
eglSwapBuffers(eglDisplay_, eglSurface_);
hasDraw = 1;
flag_ = true;
}
```
8. **结果展示**
![image.png](
https://forums-obs.openharmony.c ... yyyhyn50h2by5c6.png
"image.png")
### 总结
**可以利用OpenHarmony的hsp和har,包装组件和对应的NDK工程,提供给其他应用进行开发和应用。**
[/md]
欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/)
Powered by Discuz! X3.5