安卓平臺
本節(jié)使用OpenGL ES顯示PNG圖片,基于JNI開發(fā),使用EGL作為OpenGL ES和顯示設(shè)備之前的橋梁。另外PNG圖片的數(shù)據(jù)讀取使用libpng。
一、EGL簡介
EGL 是 OpenGL ES 和底層 Native 平臺視窗系統(tǒng)之間的接口。OpenGL ES 本質(zhì)上是一個圖形渲染管線的狀態(tài)機,而 EGL 則是用于監(jiān)控這些狀態(tài)以及維護(hù) Frame buffer 和其他渲染 Surface 的外部層。EGL提供如下機制:
- 與設(shè)備的原生窗口系統(tǒng)通信
- 查詢繪圖表面的可用類型和配置
- 創(chuàng)建繪圖表面
- 在OpenGL ES 和其他圖形渲染API之間同步渲染
- 管理紋理貼圖等渲染資源
具體的EGL函數(shù)介紹參考:EGL API文檔
二、代碼講解
顯示PNG的基本流程
- 打開PNG圖片
- 開啟線程
- 當(dāng)設(shè)置window后,在新線程中創(chuàng)建OpenGL ES運行環(huán)境,由EGL進(jìn)行配置窗口
- OpenGL ES創(chuàng)建紋理并初始化
- 創(chuàng)建顯卡可執(zhí)行程序shaders
- 開始繪制
2.1 打開PNG并開啟新線程
bool PngRender::openFile(const char *pngPath) {
LOGI("open png file:%s", pngPath);
printids();
//使用libpng打開PNG圖片,并讀取出RGBA數(shù)據(jù)
pngPicDecoder = new PngPicDecoder();
pngPicDecoder->openFile(const_cast<char *>(pngPath));
/**創(chuàng)建子線程進(jìn)行PNG解碼
* 參數(shù)一 為指向線程標(biāo)識符的指針
* 參數(shù)二 定制各種不能的線程屬性
* 參數(shù)三 是線程運行函數(shù)的地址
* 參數(shù)四 是運行函數(shù)的參數(shù)
*/
int ret = pthread_create(&threadId, 0, pngRenderThread, this);
return ret == 0;
}
使用libpng處理PNG圖片,通用性更強。新線程調(diào)用pngRenderThread函數(shù)。
2.2 新線程中初始化EGL和OpenGL
void PngRender::renderLoop() {
LOGI("renderLoop");
renderEnabled = true;
while (renderEnabled) {
LOGI("waiting for mLock...");
pthread_mutex_lock(&mLock);
LOGI("Got mLock");
switch (msg) {
case MSG_WINDOW_SET: {
LOGI("MSG_WINDOW_SET");
//設(shè)置了window,開始初始化
initial = initialize();
break;
}
case MSG_RENDER_LOOP_EXIT:
LOGI("MSG_RENDER_LOOP_EXIT");
//退出循環(huán)
renderEnabled = false;
destroy();
break;
case MSG_NONE:
LOGI("MSG_NONE");
break;
}
msg = MSG_NONE;
if (initial) {
LOGI("初始化成功,開始繪制");
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
drawPng();
pthread_cond_wait(&mCondition, &mLock);
usleep(100 * 1000);//睡眠100S
}
pthread_mutex_unlock(&mLock);
}
}
當(dāng)設(shè)置了window后,就開始執(zhí)行初始化,這個window就是安卓應(yīng)用層API,SurfaceView或者TextureView。
初始化過程:先進(jìn)行EGL的初始化,再進(jìn)行OpenGL的初始化
2.3 EGL的初始化
const EGLint attribs[] = {EGL_BUFFER_SIZE, 32,
EGL_ALPHA_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE};
//獲取顯示設(shè)備
eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOGI("獲取并與顯示設(shè)備連接");
//初始化EGL
int major,minor;
eglInitialize(eglDisplay, &major, &minor);
LOGI("初始化EGL,對EGL內(nèi)部數(shù)據(jù)結(jié)構(gòu)進(jìn)行設(shè)置,返回主版本號和次版本號,major=%d,minor=%d", major, minor);
//列出并讓EGL選擇最合適的配置
eglChooseConfig(eglDisplay, attribs, &eglConfig, 1, &numConfigs);
LOGI("選擇合適的配置參數(shù)");
EGLint eglContextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
//構(gòu)建OpenGL上下文
eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, eglContextAttributes);
LOGI("構(gòu)建OpenGL上下文");
//2.創(chuàng)建window
EGLint visualID;
eglGetConfigAttrib(eglDisplay, eglConfig, EGL_NATIVE_VISUAL_ID, &visualID);
LOGI("查詢配置對象中的屬性值,原生可視系統(tǒng)的可視ID:EGL_NATIVE_VISUAL_ID=%d", visualID);
ANativeWindow_setBuffersGeometry(nativeWindow, 0, 0, visualID);
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, 0);
LOGI("創(chuàng)建窗口");
2.4 OpenGL ES初始化
//4.創(chuàng)建紋理對象并繪制
//創(chuàng)建紋理對象
glGenTextures(1, &glTexture);
//OpenGL與紋理綁定
glBindTexture(GL_TEXTURE_2D, glTexture);
//配置紋理過濾器,決定像素如何被填充
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//配置映射規(guī)則
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//5.刷新紋理
//從pngDecoder獲取RGBA數(shù)據(jù)
const RawImageData rawImageData = pngPicDecoder->getRawImageData();
LOGI("raw_image_data Meta: width is %d height is %d size is %d colorFormat is %d",
rawImageData.width, rawImageData.height, rawImageData.size,
rawImageData.gl_color_format);
//RGBA數(shù)據(jù)傳給紋理對象
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rawImageData.width, rawImageData.height,
0, GL_RGBA, GL_UNSIGNED_BYTE, (byte *) rawImageData.data);
pngPicDecoder->releaseRawImageData(&rawImageData);//釋放臨時對象資源
//6.渲染到顯示設(shè)備
//6.1初始化shader
//6.1.1 vertext shader的編譯
GLint status;
glVertexShader = glCreateShader(GL_VERTEX_SHADER);//創(chuàng)建頂點著色器
glShaderSource(glVertexShader, 1, &VERTEX_SHADER_SOURCE, NULL);//頂點著色器程序加載到內(nèi)存
glCompileShader(glVertexShader);//編譯頂點著色器程序
glGetShaderiv(glVertexShader, GL_COMPILE_STATUS, &status);//檢查編譯是否成功
if (status == GL_FALSE) {
glDeleteShader(glVertexShader);
LOGI("Failed to compile shader : %s\n", VERTEX_SHADER_SOURCE);
return false;
}
//6.1.2 fragment shader的編譯
glFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//創(chuàng)建片著色器
glShaderSource(glFragmentShader, 1, &FRAGMENT_SHADER_SOURCE, NULL);//片元著色器加載到內(nèi)存
glCompileShader(glFragmentShader);//編譯片元著色器
glGetShaderiv(glFragmentShader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
glDeleteShader(glFragmentShader);
LOGI("Failed to compile shader : %s\n", FRAGMENT_SHADER_SOURCE);
return false;
}
//6.2 創(chuàng)建顯卡可執(zhí)行程序
glProgram = glCreateProgram();//創(chuàng)建程序?qū)ο? glAttachShader(glProgram, glVertexShader);//頂點著色器附加到程序
glAttachShader(glProgram, glFragmentShader);//片元著色器附加到程序
glBindAttribLocation(glProgram, ATTRIBUTE_VERTEX, "position");
glBindAttribLocation(glProgram, ATTRIBUTE_TEXCOORD, "texcoord");
glLinkProgram(glProgram);//鏈接程序
glGetProgramiv(glProgram, GL_LINK_STATUS, &status);//檢查鏈接是否成功
if (status == GL_FALSE) {
LOGI("Failed to link program %d", glProgram);
return false;
}
glUseProgram(glProgram);
glUniformSampler = glGetUniformLocation(glProgram, "yuvTexSampler");
2.5 繪制圖像
void PngRender::drawPng() {
LOGI("drawPng");
//規(guī)定窗口大小
glViewport(backLeft, backTop, backWidth, backHeight);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//使用顯卡繪制程序
glUseProgram(glProgram);
static const GLfloat vertices[] = {-1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f};
//設(shè)置頂點坐標(biāo)
glVertexAttribPointer(ATTRIBUTE_VERTEX, 2, GL_FLOAT, 0, 0, vertices);
glEnableVertexAttribArray(ATTRIBUTE_VERTEX);
//設(shè)置紋理坐標(biāo)
static const GLfloat texCoords[] = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};
glVertexAttribPointer(ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, 0, 0, texCoords);
glEnableVertexAttribArray(ATTRIBUTE_TEXCOORD);
//綁定gltexture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, glTexture);
glUniform1i(glUniformSampler, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
eglSwapBuffers(eglDisplay, eglSurface);
}