精华内容
下载资源
问答
  • OpenGL显示文字

    千次阅读 2019-06-23 19:33:06
    阅读这篇文章前,可以先看一下这个文字显示系列的...OpenGL提供的是图形API,本身并不提供文字处理方面的接口,如果想要显示文字就需要先将文字转化成图片,然后渲染图片。 Char Map Char Map是最简单的方式,首先...

    阅读这篇文章前,可以先看一下这个文字显示系列的其他文章,了解一些字符编码,字体相关的知识:https://blog.csdn.net/wlk1229/article/category/9008450

     

    显示文字

    OpenGL提供的是图形API,本身并不提供文字处理方面的接口,如果想要显示文字就需要先将文字转化成图片,然后渲染图片。

    Char Map

    Char Map是最简单的方式,首先需要准备号一张图片,图片中包含了要显示的文字。显示文字时只需要从图片中截取相应的纹理就可以了。这种方式比较适合文字大小固定,显示字符比较少的情况,例如显示数字,只需要“0123456789”十个字符图片就行,有时还会需要“.,”。而且所有数字的宽度一般都是一样的,这对于截取纹理来说非常容易。如下就是一张数字图片:

     

    有时我们不光需要数字,也会需要其他字符,可以使用Bitmap font软件导出相应的文字图片,软件界面如下图:

     

    软件可以导出文字使用的字体,文字的大小,加粗,斜体等等。也可设置导出图片的格式,文字之间的间隔等等,如下两张图片。

       

    在设置导出图片时,有一个导出文字的描述文件“Font descriptor”的选项,这是因为为了提高导出的图片的利用率,文字会紧密排列在图片中,为了知道每个文字在图片中的位置就需要,一个单独的文件来描述。如下,是导出的图片,和相应的描述文件:

    描述文件是一个“*.fnt”后缀名的文件,可以直接用记事本打开,从这个文件中可以知道文字的行高“lineHeigt”,字符的Unicode编码“id”,字符宽度“xadvance”,以及字符纹理在导出图片中的位置和长宽等等。有了这些信息,加上导出的图片就可以显示文字了。

     

    FreeType解析字体

    虽然Char Map的方式可以很好的显示文字,但有一定局限,1.显示的文字有限,对于英文来说字符比较少,可以用一张图片把所有字符打包,但是对于中文,要把所有字符全部打包就不是那么容易了,一般情况下只打包用到的字符;2.字体是固定的,文字的大小也是固定的,如果想更换字体则需要重新打包生成图片。Char Map本身只适合一些固定文字的显示,如果文字内容会变化,或者需要改变字体则不适合。

     

    显示文字完全可以通过解析字体文件,然后获取字体的图片,完成文字显示。只要我们有字体文件,字体文件中包含了我们要显示的文字(一般中文字体会包含所有要显示的字符),可以通过FreeType解析字体文件,获取相应的字符图片。通过FreeType解析字体,可以随意更换要显示文字的字体,也可以改变文字的大小等等。

     

    通过FreeType这个库可以很容易的从字体文件中获取到字符的位图。FreeType是一个C语言的库,支持各个平台,其提供了CMake编译,可以通过CMake软件转换成VS的工程,这里我已经编译好了VS2015中FreeType静态库的DebugRelease版本。

     

    FreeType使用也比较简单,这里给一个字符的大致步骤,详细的可以参考官方文档。

    1. 调用FT_Init_FreeType初始化FreeType库
    2. 调用FT_New_Face使用字体文件创建字体
    3. 调用FT_Select_Charmap设置映射字符编码,默认字符编码是Unicode,通常不用设置,因为大部分字体只支持Unicode和一种Apple平台的老编码。
    4. 调用FT_Set_Char_Size设置字符大小,设置字体大小时要设置字号和DPI,字号和平时用Word中字号类似,DPI是指显示设备像素密度,一般电脑显示器DPI在100左右,而手机在300左右,两者共同决定导出位图的大小。
    5. 调用FT_Set_Transform设置字体的缩放旋转和排版位置。如果想自己控制排版位置,一般可以省略这个步骤。
    6. 调用FT_Load_Char获取文字的位图信息,默认获取的是8位色深的位图,即每个字节代表一个像素。
    7. 使用位图绘制就可以显示文字了。

     

    字体缓存

    通常显示文字的时候都会有好多字符,如果每次显示文字的时候去获取一次位图,然后创建纹理,这样的效率太低。显示文字的时候会有很多重复的字符,同一个字符纹理只需要获取一次就行;另外如果每个字符创建一个纹理,这会导致创建好多纹理,所以需要一张大的纹理把需要显示的文字打包起来。因为所有显示的文字都在一个纹理中,显示文字的时候可以通过一次调用glDrawArrays渲染一个字符串,大大提高渲染效率。

    缓存字体时需要注意以下几个问题

    1. 因为像素坐标是整数,OpenGL中纹理坐标是浮点数,为了防止像素挨得太近,导致显示字符的时候出现其他字符的像素,字符之间需要留一定的空隙。
    2. OpenGL在设置纹理数据时默认是4字节对齐,而FreeType获取的是8bit位图,每个像素只有1个字节,所以需要将纹理数据对其设为一个字节,需要调用glPixelStorei(GL_UNPACK_ALIGNMENT, 1)函数。
    3. 更新字体纹理缓存中的字符是可以通过函数glTexSubImage2D只更新一个字符的数据。
    4. OpenGL中纹理坐标是以坐下角为原点,Y轴是从下到上,而FreeType获取的位图图原点是左上角,Y轴是从上到下,与OpenGL相反,所以纹理缓存中的纹理实际如下:

     

    文字边框

    FreeType获取的位图是一张刚好包只含文字的位图,不包含左右上下的空白信息。如果绘制文字时直接把每一张位图连接在一起,文字则会一个粘一个,不利于阅读,正常显示的文字上下左右都会有一定的间距。

    如上图外面的大矩形框是显示中字时需要的位置,内部红色框是FreeType获取的位图。为了正确显示文字,需要六个位置信息,图中的Height、Width、OffsetX、OffsetY已经位图的长宽。

    这六个信息可以通过以下方式获得:

    1. Height,当调用完FT_Set_Char_Size后,所有字符的高度都是一样的,在FT_Set_Char_Size设置文字大小后,可以通过fontFace->size->metrics.height/64获得,除以64说因为FreeType获取的字体高度单位的原因。
    2. Width,当调用完FT_Load_Char后,可以通过fontFace->glyph->advance.x/64,也需要除以64。
    3. OffsetX,当调用完FT_Load_Char后,为fontFace->glyph->metrics.horiBearingX/64。
    4. OffsetY,当调用完FT_Load_Char后,为(fontFace->size->metrics.height + fontFace->size->metrics.descender - fontFace->glyph->metrics.horiBearingY)/64。
    5. Bitmap宽,当调用完FT_Load_Char后,为fontFace->glyph->bitmap.width。
    6. Bitmap高,当调用完FT_Load_Char后,为fontFace->glyph->bitmap.rows。

    以上数据主要根据FreeType文档中的这张图片获得:

    其他请参考,FreeType文档

     

    除了以上信息,对于每个字符,渲染文字时还需要保存字符纹理在字体缓存纹理中的位置,所以总共需要8个位置信息,我使用了以下结构体存储:
     

    struct CharGlyphRect
    {
    	int width, height; //字符的宽高
    	int offsetX, offsetY; // 字符纹理在字符矩形中的偏移量, 坐标系为X从左到右,Y从上到下
    	int texWidth, texHeight; // 字符纹理的宽高
    	int texX, texY; //字符在缓存大纹理中的位置, 坐标系为X从左到右,Y从上到下
    	CharGlyphRect() :texWidth(0), texHeight(0), width(0), height(0), offsetX(0), offsetY(0), texX(0), texY(0) {}
    };

    为了保存所有已缓存的字符,需要使用一个map,map<wchar_t, CharGlyphRect> CharCache。

     

    渲染文字

    渲染文字时我们需要渲染一个矩形,如下图时渲染的文字与其对应的矩形:

    渲染的矩形并不是紧挨着的,以为我们的文字纹理只是刚好包含的字符的纹理,没有包括空隙,这个空隙需要我们自己控制。为了一次渲染多个文字,而文字渲染的矩形又不是紧挨着,所以不能使用GL_TRIANGLE_STRIP,只能使用GL_TRIANGLES。一个矩形需要两个三角形,所以需要六个点,六个点的坐标设置如下:
     

    	const auto& glyphRect = CharCache[str[i]];
    
    	int x = currentX + glyphRect.offsetX;
    	int y = currentY - glyphRect.offsetY;
    
    	int texX = glyphRect.texX;
    	int texY = glyphRect.texY;
    		 
    	//以下坐标是渲染时的NDC坐标
    	//左上角
    	verts[i * 6].pos[0] = PerPixelWidth * x;
    	verts[i * 6].pos[1] = PerPixelHeight * y;
    	verts[i * 6].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6].texPos[1] = texY/ static_cast<float>(CharCacheHeight);
    
    	//左下角
    	verts[i * 6 + 1].pos[0] = PerPixelWidth * x;
    	verts[i * 6 + 1].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 1].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 1].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	//右上角
    	verts[i * 6 + 2].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 2].pos[1] = PerPixelHeight * y;
    	verts[i * 6 + 2].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 2].texPos[1] = texY / static_cast<float>(CharCacheHeight);
    
    	//右上角
    	verts[i * 6 + 3].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 3].pos[1] = PerPixelHeight * y;
    	verts[i * 6 + 3].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 3].texPos[1] = texY  / static_cast<float>(CharCacheHeight);
    
    	//左下角
    	verts[i * 6 + 4].pos[0] = PerPixelWidth * x;
    	verts[i * 6 + 4].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 4].texPos[0] = texX / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 4].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	//右下角
    	verts[i * 6 + 5].pos[0] = PerPixelWidth * (x + glyphRect.texWidth);
    	verts[i * 6 + 5].pos[1] = PerPixelHeight * (y - glyphRect.texHeight);
    	verts[i * 6 + 5].texPos[0] = (texX + glyphRect.texWidth) / static_cast<float>(CharCacheWidth);
    	verts[i * 6 + 5].texPos[1] = (texY + glyphRect.texHeight) / static_cast<float>(CharCacheHeight);
    
    	currentX += glyphRect.width;
    

    平滑字体

    文字显示中有大量的曲线,如果要显示清晰的文字必须要让曲线看上去非常平滑,不能出现锯齿。

    FreeType默认获取的位图是一个8bit的位图,每个像素只有一个字节,这是一种256度灰阶图。正是这个8位的位图可以让显示的字符具有反走样的能力,可以显示平滑清晰的文字。在OpenGL中最常用的一种抗锯齿反走样方式就是利用透明来进行反走样,具体原理参考。因为FreeType提供的是灰阶图,所以可以设置一个文字颜色,然后把这个灰阶设为像素的透明度,这样显示的文字可以非常平滑,没有锯齿。

    片元着色器如下:

    #version 430

    layout(location= 2) uniform sampler2D tex; //缓存字体纹理

    layout(location= 3) uniform vec3 color; //文字颜色

     

    out vec4 outColor;

    in vec2 texCoord;

    void main()

    {

        float a = texture(tex, texCoord).r;

        outColor = vec4(color, a);

    }

     

    在绘制的时候,因为要使用透明,所以需要开启融合,代码如下:

    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2DCharCacheTex);

    glUniform1i(2, 0);

    glUniform3fv(3, 1, _color); //设置文字颜色

     

    glEnable(GL_BLEND);

    glBlendFunc(GL_SRC_ALPHAGL_ONE_MINUS_SRC_ALPHA);//设置融合函数

     

    glBindVertexArray(_vao);

    glDrawArrays(GL_TRIANGLES, 0, _vertCount);

    glDisable(GL_BLEND);

     

    以下是一个完整的例子,VS2017代码下载地址

     

    展开全文
  • OpenGL显示文字--显示汉字

    千次阅读 2017-05-10 09:45:03
    OpenGL显示文字--显示汉字   原则上,显示中文和显示英文并无不同,同样是把要显示的字符做成显示列表,然后进行调用。 但是有一个问题,英文字母很少,最多只有几百个,为每个字母创建一个显示列表,没有...

    OpenGL显示文字--显示汉字

       

    原则上,显示中文和显示英文并无不同,同样是把要显示的字符做成显示列表,然后进行调用。
    但是有一个问题,英文字母很少,最多只有几百个,为每个字母创建一个显示列表,没有问题。但是汉字有非常多个,如果每个汉字都产生一个显示列表,这是不切实际的。
    我们不能在初始化时就为每个字符建立一个显示列表,那就只有在每次绘制字符时创建它了。当我们需要绘制一个字符时,创建对应的显示列表,等绘制完毕后,再将它销毁。
    这里还经常涉及到中文乱码的问题,我对这个问题也不甚了解,但是网上流传的版本中,使用了MultiByteToWideChar这个函数的,基本上都没有出现乱码,所以我也准备用这个函数:)

    这里我略知一二:也就是说中文占两个字符,英文占一个字符,当然把两个字符的东西放到一个字符的空间里面会产生截断咯,自然不能正常显示。

    不过解决办法也不是只有一种,还有一个方法我在后面的文章中会说到。
    通常我们在C语言里面使用的字符串,如果中英文混合的话,例如“this is 中文字符.”,则英文字符只占用一个字节,而中文字符则占用两个字节。用MultiByteToWideChar函数,可以转化为所有的字符都占两个字节(同时解决了前面所说的乱码问题:))。

    (注:这里会有另一个问题:对于英文字符来说也用两个字节会造成不必要的空间浪费,我们理想的状态应该是英文只用一个字节,汉字用两个(这是不是可以实现呢?))
    转化的代码如下:

    // 计算字符的个数
    // 如果是双字节字符的(比如中文字符),两个字节才算一个字符
    // 否则一个字节算一个字符
    len = 0;
    for(i=0; str[i]!='\0'; ++i)
    {
            if( IsDBCSLeadByte(str[i]) )
                     ++i;
             ++len;
    }

    // 将混合字符转化为宽字符
    wstring = ( wchar_t*) malloc((len+1) *  sizeof( wchar_t));
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1,  wstring, len);
    wstring[len] = L'\0';

    // 用完后记得释放内存
    free( wstring);


    加上前面所讲到的wglUseFontBitmaps函数,即可显示中文字符了。
    void drawCNString( const  char* str) {
            int len, i;
            wchar_twstring;
             HDC hDC = wglGetCurrentDC();
             GLuint  list = glGenLists(1);

             // 计算字符的个数
             // 如果是双字节字符的(比如中文字符),两个字节才算一个字符
             // 否则一个字节算一个字符
             len = 0;
            for(i=0; str[i]!='\0'; ++i)
             {
                    if( IsDBCSLeadByte(str[i]) )
                             ++i;
                     ++len;
             }

             // 将混合字符转化为宽字符
            wstring = ( wchar_t*) malloc((len+1) *  sizeof( wchar_t));
             MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1,  wstring, len);
            wstring[len] = L'\0';

             // 逐个输出字符
            for(i=0; i<len; ++i)
             {
                     wglUseFontBitmapsW(hDC,  wstring[i], 1,  list);
                     glCallList( list);
             }

             // 回收所有临时资源
            free( wstring);
             glDeleteLists( list, 1);
    }


    注意我用了wglUseFontBitmapsW函数,而不是wglUseFontBitmaps。wglUseFontBitmapsW是wglUseFontBitmaps函数的宽字符版本,它认为字符都占两个字节。因为这里使用了MultiByteToWideChar,每个字符其实是占两个字节的,所以应该用wglUseFontBitmapsW。
    void display( void) {
             glClear(GL_COLOR_BUFFER_BIT);

             selectFont(48, ANSI_CHARSET,  "Comic Sans MS");
             glColor3f(1.0f, 0.0f, 0.0f);
             glRasterPos2f(-0.7f, 0.4f);
             drawString( "Hello, World!");

             selectFont(48, GB2312_CHARSET,  "楷体_GB2312");
             glColor3f(1.0f, 1.0f, 0.0f);
             glRasterPos2f(-0.7f, -0.1f);
             drawCNString( "当代的中国汉字");

             selectFont(48, DEFAULT_CHARSET,  "华文仿宋");
             glColor3f(0.0f, 1.0f, 0.0f);
             glRasterPos2f(-0.7f, -0.6f);
             drawCNString( "傳統的中國漢字");

             glutSwapBuffers();
    }

    效果如图:
    http://blog.programfan.com/upfile/200805/20080505132632.gif
    原文链接:http://blog.sina.com.cn/s/blog_4ff085000100dew0.html
    展开全文
  • GTK4+OpenGl显示文字

    2021-04-17 23:23:09
    使用GTK4+OpenGL+FreeType2 显示文字
  • OpenGL,显示汉字

    2018-10-30 07:32:31
    OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了
  • QtOpenGL文字显示

    2014-08-23 13:40:24
    Qt版的OpenGLDemo,其中包含透视投影,纹理贴图,文字显示,反锯齿。
  • NULL 博文链接:https://kungsoft.iteye.com/blog/318875
  • OpenGL显示文字--显示字符

    万次阅读 2016-06-25 14:04:19
    OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。 各种流行的图形操作系统,例如Windows系统和Linux系统,都提供了一些功能,以便...

    OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。
    各种流行的图形操作系统,例如Windows系统和Linux系统,都提供了一些功能,以便能够在OpenGL程序中方便的显示文字。
    最常见的方法就是,我们给出一个字符,给出一个显示列表编号,然后操作系统由把绘制这个字符的OpenGL命令装到指定的显示列表中。当需要绘制字符的时候,我们只需要调用这个显示列表即可。
    不过,Windows系统和Linux系统,产生这个显示列表的方法是不同的(虽然大同小异)。作为我个人,只在Windows系统中编程,没有使用Linux系统的相关经验,所以本课我们仅针对Windows系统。

    OpenGL版的“Hello, World!”
    写完了本课,我的感受是:显示文字很简单,显示文字很复杂。看似简单的功能,背后却隐藏了深不可测的玄机。
    呵呵,别一开始就被吓住了,让我们先从“Hello, World!”开始。
    前面已经说过了,要显示字符,就需要通过操作系统,把绘制字符的动作装到显示列表中,然后我们调用显示列表即可绘制字符。
    假如我们要显示的文字全部是ASCII字符,则总共只有0到127这128种可能,因此可以预先把所有的字符分别装到对应的显示列表中,然后在需要时调用这些显示列表。
    Windows系统中,可以使用wglUseFontBitmaps函数来批量的产生显示字符用的显示列表。函数有四个参数:
    第一个参数是HDC,学过Windows GDI的朋友应该会熟悉这个。如果没有学过,那也没关系,只要知道调用wglGetCurrentDC函数,就可以得到一个HDC了。具体的情况可以看下面的代码。
    第二个参数表示第一个要产生的字符,因为我们要产生0到127的字符的显示列表,所以这里填0。
    第三个参数表示要产生字符的总个数,因为我们要产生0到127的字符的显示列表,总共有128个字符,所以这里填128。
    第四个参数表示第一个字符所对应显示列表的编号。假如这里填1000,则第一个字符的绘制命令将被装到第1000号显示列表,第二个字符的绘制命令将被装到第1001号显示列表,依次类推。我们可以先用glGenLists申请128个连续的显示列表编号,然后把第一个显示列表编号填在这里。
    还要说明一下,因为wglUseFontBitmaps是Windows系统特有的函数,所以在使用前需要加入头文件:#include <windows.h>。
    现在让我们来看具体的代码:
    #include <windows.h>

    // ASCII字符总共只有0到127,一共128种字符
    #define MAX_CHAR        128

    void drawString( const  char* str) {
         static  int isFirstCall = 1;
         static GLuint lists;

         if( isFirstCall ) { // 如果是第一次调用,执行初始化
                             // 为每一个ASCII字符产生一个显示列表
             isFirstCall = 0;

             // 申请MAX_CHAR个连续的显示列表编号
             lists = glGenLists(MAX_CHAR);

             // 把每个字符的绘制命令都装到对应的显示列表中
             wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
         }
         // 调用每个字符对应的显示列表,绘制每个字符
         for(; *str!='/0'; ++str)
             glCallList(lists + *str);
    }


    显示列表一旦产生就一直存在(除非调用glDeleteLists销毁),所以我们只需要在第一次调用的时候初始化,以后就可以很方便的调用这些显示列表来绘制字符了。
    绘制字符的时候,可以先用glColor*等指定颜色,然后用glRasterPos*指定位置,最后调用显示列表来绘制。
    void display( void) {
         glClear(GL_COLOR_BUFFER_BIT);

         glColor3f(1.0f, 0.0f, 0.0f);
         glRasterPos2f(0.0f, 0.0f);
         drawString( "Hello, World!");

         glutSwapBuffers();
    }


    _glRasterPos中的参数并不是屏幕坐标,而是世界坐标,这就是为什么我以前如果用(0,0)作为参数的话,位置是在屏幕的中心

    展开全文
  • opengl显示汉字

    热门讨论 2009-03-30 18:33:33
    可以在opengl下很好地显示汉字,包括3D文字
  • 使用freetype于opengl显示汉字 改编的东西 :)
  • OpenGL基于Unicode和ASCII做出3D汉字和英文字符的效果,只有一个代码文件,用VisualStudio2013即可成功编译运行!
  • opengl显示RGB数据,自己测试代码,虽杂乱但是比较简单,可以从中扣自己要的代码。
  • opengl 叠加显示文字

    2021-06-16 17:24:43
    《android mediacodec 编码demo(java)》 《NDK中使用mediacodec编码h264》 《Android native 层使用opengl渲染YUV420p和NV12》 《android 使用NativeWindow渲染RGB视频》 《opengl 叠加显示文字》 《android studio ...

    Ndk中使用Mediacode解码
    android mediacodec 编码demo(java)
    NDK中使用mediacodec编码h264
    Android native 层使用opengl渲染YUV420p和NV12
    android 使用NativeWindow渲染RGB视频
    opengl 叠加显示文字
    android studio 编译freeType
    最原始的yuv图像叠加文字的实现--手动操作像素


    续上一篇《Android native 层使用opengl渲染YUV420p和NV12
    这里想在渲染之前,叠加显示帧率分辨率等等实时的信息。
    大概有如下方案(个人见解):
    一: 渲染之前直接修改原raw帧,把文字的图形叠加到raw帧图像上去。
    1.0 手动修改像素的方式:获取到文字的位图之后,最好也是同我们渲染的视频帧数据一样也是yuv格式的,然后直接操作渲染之前的yuv视频帧,将文字图像数据部分逐个像素逐个像素地赋值到视频帧数据里面去就完成了覆盖。 这是最原始的手动方式,看起来cpu运算量也不小,还要求文字的图像和视频帧数据格式一致。
    2.0 搞个ffmpeg,利用里面的drawtext 滤镜画上去就行了。(当然需要移植ffmpeg)
    二:不修改视频raw帧数据,额外添加一个纹理给文字图像使用,在opengl显卡程序里面利用显卡的计算能力,将渲染之前的rgb数据和文字图像的这个纹理混合,再渲染


    这里使的第二个方式,不修改视频原始yuv数据,在opengl里面混合。
    怎么获取文字的图形?
    有书中介绍,回调java层的接口,利用android的api,canvs绘制文字得到图形,这里并不想这么做,感觉又依赖了太多的东西,只是想纯粹的c++实现。所以,移植了个freeType字库,直接在c++层利用freeType字库将字符串转换成一个 rgba32的序列文件。
    如下代码的原理,先准备一个大的“低图”(画板),对每一个字符,都需要从字库里面单独提取出其bitmap图,然后我们遍历这个图的像素,更具每一个像素的不透明度,来赋值底图指定位置的像素值,这个时候我们就可以自定义颜色,文字背景就设置alpth--不透明度值为0. 每次更新完一个字符的图,就更新下在“底图”上的位置,就类似于画板。(如下代码有点缺陷,还没有支持到unicode 字符,只是支持ASCII)

    怎么在opengl里面混合?
    我们希望文字的背景是透明的,不会产生一个方块,就利用alpth不透明度这个分量,混合的时候rgb的值需要有这个alpth加权。(如下代码,还有缺陷,当前准备的文字图像纹理和视频纹理必须是大小一致的,对每一个片段都会进行混合的处理,这样其实比较浪费性能,比较文字部分只占整个图像的一小块)
    两张图片叠加,文字的在上层,不透明度为0的像素就不显示,漏出底层的图像,会photoshop就很好理解,和里面的图层的概念一样。

    上代码:
    使用freeTyepe获取文字图形代码:

    DrawText.cpp
    //
    // Created by xiancan.wang on 6/3/21.
    //
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <ft2build.h>
    #include FT_FREETYPE_H
    #include "DrawText.h"
    #include "logs.h"
    struct freetype_data{
        FT_Library    library;
        FT_Face       face;
    
        FT_GlyphSlot  slot;
        FT_Matrix     matrix;                 /* transformation matrix */
        FT_Vector     pen;                    /* untransformed origin  */
    }globalContext;
    
    
    
    
    /* Replace this function with something useful. */
    
    void draw_bitmap( FT_Bitmap*  bitmap,unsigned char*out,
                      FT_Int      x,
                      FT_Int      y, int w, int h,
                      char r,char g,char b )
    {
    //rgba
        FT_Int  i, j, p, q;
        FT_Int  x_max = x + bitmap->width;
        FT_Int  y_max = y + bitmap->rows;
        int pixByte=4;
    
    //    for ( i = x, p = 0; i < x_max; i++, p++ )
    //    {//行
    //        for ( j = y, q = 0; j < y_max; j++, q++ )
    //        {//列
    //            if ( i < 0 || j < 0  ||i >= w || j >= h )
    //                continue;
    //
    //            out[(j*w+i)*pixByte+3] = bitmap->buffer[q * bitmap->width + p];
    //    //rgba
    //            out[(j*w+i)*pixByte] = r;
    //            out[(j*w+i)*pixByte+1] = g;
    //            out[(j*w+i)*pixByte+2] =b;
    //        }
    //    }
    
    
    //先列后行,访问连续内存,对于现在的内存来说基本上也没有提高效率
        for ( j = y, q = 0; j < y_max; j++, q++ ) {
            //列
            for (i = x, p = 0; i < x_max; i++, p++) {
                //行
                if (i < 0 || j < 0 || i >= w || j >= h)
                    continue;
    #if 0
                out[(j * w + i) * pixByte + 3] = bitmap->buffer[q * bitmap->width + p];
                //rgba
                out[(j * w + i) * pixByte] = r;
                out[(j * w + i) * pixByte + 1] = g;
                out[(j * w + i) * pixByte + 2] = b;
    #endif
                unsigned char alth_vale = bitmap->buffer[q * bitmap->width + p];
                double alth =(alth_vale*1.0)/255;
                out[(j*w+i)*pixByte]   = r*alth;
                out[(j*w+i)*pixByte+1] = g*alth;
                out[(j*w+i)*pixByte+2] = b*alth;
                out[(j*w+i)*pixByte+3] = alth_vale;
    
            }
        }
    }
    
    int DT_init(const char*fontfile){
        FT_Error      error;
    
        error = FT_Init_FreeType( &globalContext.library );              /* initialize library */
        /* error handling omitted */
        if(error!=0){
            ALOGD("FT_Init_FreeType erro:%d",error);
        }
        error = FT_New_Face( globalContext.library, fontfile, 0, &globalContext.face ); /* create face object */
        /* error handling omitted */
        if(error!=0){
            ALOGD("FT_New_Face erro:%d",error);
        }
    #if 0
        /* use 50pt at 100dpi */
      error = FT_Set_Char_Size( globalContext.face, 80 * 64, 0,
                                100, 0 );                /* set character size */
    	/* pixels = 50 /72 * 100 = 69  */
    #else
        //FT_Set_Pixel_Sizes(globalContext.face, 24, 0);
    #endif
        globalContext.slot = globalContext.face->glyph;
    
        /* set up matrix */
        double  angle         = ( 0.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
        globalContext.matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
        globalContext.matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
        globalContext.matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
        globalContext.matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
    
        return error;
    
    }
    
    int DT_deinit(){
        FT_Done_Face    ( globalContext.face );
        FT_Done_FreeType( globalContext.library);
        return 0;
    }
    
    int DT_GetBitmapFromText (const char*text,unsigned char* bitmapOut,int w, int h,int fontSize){
    
        int size = strlen(text);
        //	FT_Set_Pixel_Sizes(globalContext.face, 24, 0);
        FT_Set_Char_Size( globalContext.face, 64 * fontSize, 0,
                          100, 0 );                /* set character size */
        /* start at (0,40) relative to the upper left corner  */
        globalContext.pen.x = 0 * 64;
        globalContext.pen.y = 0 * 64;
        FT_Error error;
        int i =0;
        for ( i = 0; i < size; i++ )
        {
    
            /* set transformation */
            FT_Set_Transform( globalContext.face, &globalContext.matrix, &globalContext.pen );
    
            /* load glyph image into the slot (erase previous one) */
            error = FT_Load_Char( globalContext.face, text[i], FT_LOAD_RENDER );
            if ( error )
                continue;                 /* ignore errors */
    
            /* now, draw to our target surface (convert position) */
    
            FT_Bitmap * btMap = &globalContext.slot->bitmap;
            printf("[%d]%c x:%d y:%d w:%d, h:%d\n",i,text[i],globalContext.slot->bitmap_left,globalContext.slot->bitmap_top,btMap->width,btMap->rows);
    //        draw_bitmap( &globalContext.slot->bitmap,
    //                     bitmapOut,
    //                     globalContext.slot->bitmap_left,
                        //每一个字母顶部对齐
    //                     0,w,h,0xff,0xff,0x00);
    
    //        draw_bitmap( &globalContext.slot->bitmap,
    //                     bitmapOut,
    //                     globalContext.slot->bitmap_left,
    //                     //底部对齐
    //                     (h-globalContext.slot->bitmap_top),w,h,0xff,0xff,0x00);
    
            draw_bitmap( &globalContext.slot->bitmap,
                         bitmapOut,
                         globalContext.slot->bitmap_left,
                    //底部对齐 向上平移
                   // (h-globalContext.slot->bitmap_top)-(h-fontSize),
                         fontSize-globalContext.slot->bitmap_top,
                         w,h,0xff,0xff,0x00);//指定颜色
            /* increment pen position */
            globalContext.pen.x += globalContext.slot->advance.x;
            //globalContext.pen.y += globalContext.slot->advance.y;
        }
    
        int imgW= globalContext.slot->bitmap_left+ globalContext.slot->bitmap.width;
        return imgW;
    }
    
    
    unsigned char * DT_GetBitmapFromTextAutoWidth(const char*text, int fontSize, int *w, int *h){
        int len = strlen(text);
        int tempW=fontSize*len*2;
        int tempH=fontSize;
        // some img wider than fontSize, so we just alloc double
        unsigned char *img=(unsigned char*)malloc(fontSize*len*2);
        int imgW= DT_GetBitmapFromText(text,img,tempW,tempH,fontSize);
        if(w){
            *w=imgW;
        }
        if(h){
            *h=tempH;
        }
    
        return img;
    }
    //DrawText.h
    //
    // Created by xiancan.wang on 6/3/21.
    //
    
    #ifndef TEST_CMAKE_DRAWTEXT_H
    #define TEST_CMAKE_DRAWTEXT_H
    int DT_init(const char*fontfile);
    int DT_deinit();
    int DT_GetBitmapFromText (const char*text,unsigned char* bitmapOut,int w, int h,int fontSize);
    
    #endif //TEST_CMAKE_DRAWTEXT_H
    

    opengl渲染:
     

    GlPrograme.cpp
    //canok 2021.0528
    #include <stdlib.h>
    #include "GlPragrame.h"
    #include "Openglfuncs.h"
    #include "logs.h"
    #define GET_STR(x) #x
    struct S_CONTEXT global_Context = {.bufMutex=PTHREAD_MUTEX_INITIALIZER,.mCondMutex=PTHREAD_MUTEX_INITIALIZER,
    .mCond=PTHREAD_COND_INITIALIZER};
    
    const char *vertexShader_ = GET_STR(
    attribute vec4 aPosition;//输入的顶点坐标,会在程序指定将数据输入到该字段
    attribute vec2 aTextCoord;//输入的纹理坐标,会在程序指定将数据输入到该字段
    varying vec2 vTextCoord;//输出的纹理坐标
    void main() {
        //这里其实是将上下翻转过来
        vTextCoord = vec2(aTextCoord.x, 1.0 - aTextCoord.y);
        //直接把传入的坐标值作为传入渲染管线。gl_Position是OpenGL内置的
        gl_Position = aPosition;
    }
    );
    /*
    The difference between the three single-component texture formats, GL_ALPHA, GL_LUMINANCE and GL_INTENSITY, is in the way the four-component RGBA color vector is generated. If the value for a given texel is X, then the RGBA color vector generated is:
    
        * GL_ALPHA: RGBA = (0, 0, 0, X0)
    
        * GL_LUMINANCE: RGBA = (X0, X0, X0, 1)
    
        * GL_INTENSITY: RGBA = (X0, X0, X0, X0)
    
        * GL_LUMINANCE_ALPHA: RGBA = (X0, X0, X0, X1), 这个才是二维交叉的
        * GL_RGB :          RGBA=(X0, X1, X2, 1),三个量交叉
        * GL_RGBA :          RGBA=(X0, X1, X2, X3),四个量交叉
        这里用的 GL_LUMINANCE,每一纹理分量  r g b 是一样的值,都是纹理设定的数据
        其实我们可以改用GL_LUMINANCE_ALPHA, 只需要一个量
    
          这里整个的原理,即把YUV420S  每一个分量当做一个纹理,然后在片原着色器中
          利用显卡的高速运算,通过 yuv转rgb 的矩阵公式,把三个纹理分量的值计算转换成rgb
          接着,然后画这个rgba 的点。
    */
    
    const char *fragYUV420P_ = GET_STR(
        precision mediump float;
        varying vec2 vTextCoord;
        //输入的yuv三个纹理
        uniform sampler2D yTexture;//采样器
        uniform sampler2D uTexture;//采样器
        uniform sampler2D vTexture;//采样器
        uniform sampler2D infoTexture;
        void main() {
            vec3 yuv;
            vec3 rgb;
            vec4 info;
            //分别取yuv各个分量的采样纹理
            //下面是一个 yuv转 rgb的矩阵运算
            // 纹理的 r g b相等,都是一个值
            /*
            *矩阵公式
            *【】x [[yuv] -[0,0.5,0.5]]
            */
            yuv.x = texture2D(yTexture, vTextCoord).r;
            yuv.y = texture2D(uTexture, vTextCoord).r - 0.5;
            yuv.z = texture2D(vTexture, vTextCoord).r - 0.5;
            rgb = mat3(
                    1.0, 1.0, 1.0,
                    0.0, -0.39465, 2.03211,
                    1.13983, -0.5806, 0.0
            ) * yuv;
    
            //混合文字
            info = texture2D(infoTexture, vTextCoord);
            rgb.x = info.r*info.a + (1.0 - info.a)*rgb.x;
            rgb.y = info.g*info.a + (1.0 - info.a)*rgb.y;
            rgb.z = info.b*info.a + (1.0 - info.a)*rgb.z;
            //gl_FragColor是OpenGL内置的
            gl_FragColor = vec4(rgb, 1.0);
        }
    );
    
    const char *fragNV12_ = GET_STR(
        precision mediump float;
        varying vec2 vTextCoord;
        uniform sampler2D yTexture;
        uniform sampler2D uvTexture;
        uniform sampler2D infoTexture;
        void main(void)
        {
            vec3 yuv; 
            vec3 rgb; 
            vec4 info;
            yuv.x = texture2D(yTexture, vTextCoord.st).r; 
            yuv.y = texture2D(uvTexture, vTextCoord.st).r - 0.5; 
            yuv.z = texture2D(uvTexture, vTextCoord.st).a - 0.5; 
            rgb = mat3( 1,       1,         1, 
                        0,       -0.39465,  2.03211, 
                        1.13983, -0.58060,  0) * yuv; 
    
            info = texture2D(infoTexture, vTextCoord);
            rgb.x = info.r*info.a + (1.0 - info.a)*rgb.x;
            rgb.y = info.g*info.a + (1.0 - info.a)*rgb.y;
            rgb.z = info.b*info.a + (1.0 - info.a)*rgb.z;
            gl_FragColor = vec4(rgb, 1.0); 
        }
    );
    
    
    /**
     * 加载  着色器
     * @param type     着色器类型
     * @param shaderSrc  着色源码
     * @return
     */
    GLuint LoadShader(GLenum type, const char *shaderSrc) {
        GLuint shader;
        GLint compiled;
        shader = glCreateShader(type);  //  创建 着色器 句柄
        if (shader == 0) {
            ALOGE("create shader error");
            return 0;
        }
    
        // 装载 着色器源码
        glShaderSource(shader, 1, &shaderSrc, 0);
    
        // 编译着色器
        glCompileShader(shader);
    
        // 检测编译状态
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    
        if (!compiled) {
            GLint infoLen = 0;
            // 获取日志长度
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen > 1) {
                GLchar *infoLog = (GLchar *)(malloc(sizeof(GLchar) * infoLen));
                // 获取日志 信息
                glGetShaderInfoLog(shader, infoLen, 0, infoLog);
                ALOGE("%s", infoLog);
                free(infoLog);
            }
            glDeleteShader(shader);
            return 0;
        }
        return shader;
    }
    
    GLuint LoadProgramYUV(int type){
    
        GLint vertexShader = LoadShader(GL_VERTEX_SHADER,vertexShader_);
        GLint fragShader;
        if(TYPE_YUV420SP_NV12 == type){
           fragShader = LoadShader(GL_FRAGMENT_SHADER,fragNV12_);
        }else{
        //默认用yuv420p
            fragShader = LoadShader(GL_FRAGMENT_SHADER,fragYUV420P_);
        }
        // 创建渲染程序
        GLint program = glCreateProgram();
        if (program == 0){
            ALOGE("YUV : CreateProgram failure");
            glDeleteShader(vertexShader);
            glDeleteShader(fragShader);
            return 0;
        }
    
        // 先渲染程序中 加入着色器
        glAttachShader(program,vertexShader);
        glAttachShader(program,fragShader);
    
        // 链接 程序
        glLinkProgram(program);
        GLint  status = 0;
        glGetProgramiv(program,GL_LINK_STATUS,&status);
        if (status == 0){
            ALOGE("glLinkProgram failure");
            glDeleteProgram(program);
            return 0;
        }
        global_Context.glProgram = program;
        glUseProgram(program);
        return 1;
    
    }
    enum LAYOUT {
        LAYOUT_2x2,
        LAYOUT_3x3,
        LAYOUT_6,
    
    };
    void GetLayout();
    void RenderInfoConfig(int width,int height){
        glUniform1i(glGetUniformLocation(global_Context.glProgram, "infoTexture"), 3);
        glGenTextures(1, &global_Context.mInfoTexture);
        glBindTexture(GL_TEXTURE_2D, global_Context.mInfoTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D,
                     0,
                     GL_RGBA,
                     width ,
                     height ,
                     0,
                     GL_RGBA,
                     GL_UNSIGNED_BYTE,
                     NULL
        );
    }
    void RenderYUVConfig(uint width, uint height, int type) {
        static float ver[] = {
                1.0f, -1.0f, 0.0f,
                -1.0f, -1.0f, 0.0f,
                1.0f, 1.0f, 0.0f,
                -1.0f, 1.0f, 0.0f
        };
    
    //    static float ver[] = {
    //            0.0f, -1.0f, 0.0f,
    //            -1.0f, -1.0f, 0.0f,
    //            0.0f, 0.0f, 0.0f,
    //            -1.0f, 0.0f, 0.0f
    //
    //            //这里加入画info 纹理的坐标
    //
    //    };
        GLuint apos = (GLuint)(glGetAttribLocation(global_Context.glProgram, "aPosition"));
        glEnableVertexAttribArray(apos);
        glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 0, ver);
    
       //加入纹理坐标数据  //纹理坐标和 上面的顶点左边一一对应,
        static float fragment[] = {
                1.0f, 0.0f,
                0.0f, 0.0f,
                1.0f, 1.0f,
                0.0f, 1.0f
        };
    //    static float fragment[] = {
    //            2.0f, 0.0f,
    //            0.0f, 0.0f,
    //            2.0f, 2.0f,
    //            0.0f, 2.0f
    //    };
        GLuint aTex = (GLuint)(glGetAttribLocation(global_Context.glProgram, "aTextCoord"));
        glEnableVertexAttribArray(aTex);
        glVertexAttribPointer(aTex, 2, GL_FLOAT, GL_FALSE, 0, fragment);
    
        ALOGD("can_ok[%s%d] type:%d",__FUNCTION__,__LINE__,type);
    
        if(TYPE_YUV420SP_NV12 == type){
            ALOGD("can_ok[%s%d] type:%d",__FUNCTION__,__LINE__,type);
    
    
    
            glUniform1i(glGetUniformLocation(global_Context.glProgram, "yTexture"), 0);
            glUniform1i(glGetUniformLocation(global_Context.glProgram, "uvTexture"), 1);
    
    
            glGenTextures(2, global_Context.mTextures);
    
            glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_LUMINANCE,
                         width,
                         height,
                         0,
                         GL_LUMINANCE,
                         GL_UNSIGNED_BYTE,
                         NULL 
            );
    
            glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_LUMINANCE_ALPHA, 
                         width / 2,
                         height / 2,
                         0, 
                         GL_LUMINANCE_ALPHA,
                         GL_UNSIGNED_BYTE,
                         NULL 
            );
    
    
        }else{
            glUniform1i(glGetUniformLocation(global_Context.glProgram, "yTexture"), 0);
            glUniform1i(glGetUniformLocation(global_Context.glProgram, "uTexture"), 1);
            glUniform1i(glGetUniformLocation(global_Context.glProgram, "vTexture"), 2);
    
            glGenTextures(3, global_Context.mTextures);
    
            glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_LUMINANCE,
                         width,
                         height,
                         0,
                         GL_LUMINANCE,
                         GL_UNSIGNED_BYTE,
                         NULL
            );
    
            glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_LUMINANCE,
                         width / 2,
                         height / 2,
                         0,
                         GL_LUMINANCE,
                         GL_UNSIGNED_BYTE,
                         NULL
            );
    
            glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[2]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D,
                         0,
                         GL_LUMINANCE,
                         width / 2,
                         height / 2,
                         0,
                         GL_LUMINANCE,
                         GL_UNSIGNED_BYTE,
                         NULL
            );
     
        }
    }
    void RendInfo(GLuint width, GLuint height, unsigned char *buf){
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, global_Context.mInfoTexture);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width, height,
                        GL_RGBA, GL_UNSIGNED_BYTE,
                        buf);
    }
    //平面格式
    void RenderYUV420P(GLuint width, GLuint height, unsigned char *buf) {
    
        //Y
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width, height,
                        GL_LUMINANCE, GL_UNSIGNED_BYTE,
                        buf);
    
    
        //U
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width / 2, height / 2,
                        GL_LUMINANCE, GL_UNSIGNED_BYTE,
                        buf+width*height);
    
    
        //V
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[2]);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width / 2, height / 2,
                        GL_LUMINANCE, 
                        GL_UNSIGNED_BYTE,
                        buf+width*height*5/4);
    
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        //eglSwapBuffers(global_Context.eglDisplay, global_Context.eglSurface);
    
    }
    
    void RenderNV12(GLuint width, GLuint height, unsigned char * buf){
         //Y
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[0]);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width, height,
                        GL_LUMINANCE,
                        GL_UNSIGNED_BYTE,
                        buf);
    
        //UV
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, global_Context.mTextures[1]);
        glTexSubImage2D(GL_TEXTURE_2D, 0,
                        0, 0,
                        width/2, height / 2,
                        GL_LUMINANCE_ALPHA,
                        GL_UNSIGNED_BYTE,
                        buf+width*height);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    }
    
    
    void createprograme(){
        LoadProgramYUV(global_Context.type);
        RenderYUVConfig(global_Context.mW,global_Context.mH,global_Context.type);
        RenderInfoConfig(global_Context.mW,global_Context.mH);
    }
    void drawFrame(unsigned char* buf, int size) {
        if(global_Context.type == TYPE_YUV420SP_NV12){
             ALOGD("[%s%d] render NV12 %dx%d",__FUNCTION__,__LINE__,global_Context.mW,global_Context.mH);
             RenderNV12(global_Context.mW,global_Context.mH,buf);
        }else{
             ALOGD("[%s%d]render YUV420P %dx%d",__FUNCTION__,__LINE__,global_Context.mW,global_Context.mH);
             RenderYUV420P(global_Context.mW,global_Context.mH,buf);
        }
    }
    void updateInfo(unsigned char* buf, int size){
        RendInfo(global_Context.mW,global_Context.mH,buf);
    }
    
    void changeLayout(int width, int height){
        glViewport(0,0,width,height);
    }
    
    GlPrograme.h
    #ifndef __GLPRAGRAME2__H__
    #define __GLPRAGRAME2__H__
    #include <jni.h>
    #include<EGL/egl.h>
    #include<EGL/eglext.h>
    #include <android/log.h>
    #include <GLES2/gl2.h>
    #include <GLES2/gl2ext.h>
    
    #include <pthread.h>
    #include <unistd.h>
    struct S_CONTEXT{
        GLint glProgram;
        GLuint mTextures[3];
        GLuint mInfoTexture;
        int mW;
        int mH;
        int type;
        int mfps;
        ANativeWindow *nwin ;
        unsigned char* buf;
        int buflen;
        unsigned char*infobuf;
        int infobuflen;
        pthread_mutex_t bufMutex;
        pthread_mutex_t mCondMutex;
        pthread_cond_t mCond;
        pthread_t mThread;
        char *file_in;
    } ;
    extern struct S_CONTEXT global_Context;
    void createprograme();
    void drawFrame(unsigned char* buf, int size) ;
    void updateInfo(unsigned char* buf, int size);
    
    #endif
    

    EGL 环境
     

    Openglfuncs.cpp
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include "logs.h"
    #include "GlPragrame.h"
    #include "Openglfuncs.h"
    #include "DrawText.h"
    #define MIN(x,y) (x)<(y)?(x):(y)
    
    #define DEBUG_FROME_FILE 1
    EGLDisplay megldisplay =NULL;
    EGLSurface meglSurface =NULL;
    EGLContext meglContext=NULL;
    int mbRun=0;
    
    
    int64_t getNowUs(){
    #if 1
        timeval tv;
        gettimeofday(&tv, 0);
        return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec;
    #else
        return ALooper::GetNowUs();
    #endif
    }
    void initGles(){
        // 1 EGL 初始化
        EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if(display == EGL_NO_DISPLAY){
            ALOGE("eglGetDisplay failed %d", eglGetError());
            return;
        }
        megldisplay = display;
        if(!eglInitialize(display,NULL,NULL)){
            ALOGE("eglInitialize failed %d", eglGetError());
        }
    
        //2配置display
        EGLConfig config;
        EGLint configNum;
        EGLint configSpec[] = {
                EGL_RED_SIZE,8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_ALPHA_SIZE,8,
                EGL_RENDERABLE_TYPE,EGL_OPENGL_ES2_BIT,
                EGL_SURFACE_TYPE,EGL_WINDOW_BIT,EGL_NONE
        };
    
        if(EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)){
            ALOGD("eglChooseConfig failed!");
            return;
        }
    
    
        //3 EGLcontext 创建上下文
        const EGLint ctxAttr[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,EGL_NONE
        };
        EGLContext  context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
        if(context == EGL_NO_CONTEXT){
            ALOGD("eglCreateContext failed!");
            return;
        }
        meglContext=context;
    
    
        //4创建EGLsurface,把 java端传入的surface和EGL关联起来
        EGLSurface  eglsurface = eglCreateWindowSurface(display, config, global_Context.nwin, 0);
        if(eglsurface == EGL_NO_SURFACE){
            ALOGD("eglCreateWindowSurface failed!");
            return;
        }
        meglSurface=eglsurface;
    
        if(EGL_TRUE != eglMakeCurrent(display, eglsurface, eglsurface, context)){
            ALOGD("eglMakeCurrent failed!");
            return ;
        }
    
        glClearColor(0.0f,0.0f,0.0f,1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        eglSwapBuffers(megldisplay, meglSurface);
    }
    
    
    void *renderthread(void * prame){
    #if DEBUG_FROME_FILE
        //第一步搭建ES环境
        initGles();
        ALOGD("mygl_init intglescontext ok ");
        //第二步 创建显卡可执行程序
        createprograme();
        ALOGD("mygl_init createprograme ok %d",global_Context.type);
    
        //waring!!!!!!!!
        //glViewport(0,0,WIDTH,HEIGHT);
    
        FILE *fp = fopen(global_Context.file_in,"r");
        if(fp ==NULL){
            ALOGD("fopen failed!");
            return NULL;
        }
        global_Context.buflen=global_Context.mW*global_Context.mH*3/2;
        global_Context.buf = (unsigned char*)malloc( global_Context.buflen);
    
        global_Context.infobuflen=global_Context.mW*global_Context.mH*4;
        global_Context.infobuf = (unsigned char*)malloc( global_Context.infobuflen);
        memset(global_Context.infobuf,0,global_Context.infobuflen);
    
        int error=0;
        error = DT_init("/storage/emulated/0/hwyuanti.TTF");
        ALOGD("DT_init:%d",error);
        int i=0,j=0;
        for(i=0;i<100;i++){
            for(j=0;j<global_Context.mW*4;j+=4){
                global_Context.infobuf[i*global_Context.mW+j]=0xff;
                global_Context.infobuf[i*global_Context.mW+j+3]=0xff;
            }
        }
    
    
        int ret =0;
        char info[128]={0};
        int64_t framecount =0;
        while(mbRun){
    
            //这里绘制
            if( (ret = fread(global_Context.buf,1,global_Context.buflen,fp)) ==global_Context.buflen ){
                int64_t time1=getNowUs();
                // snprintf(info,sizeof(info),"time:%ld",getNowUs());
                snprintf(info,sizeof(info),"%ld",framecount++);
                memset(global_Context.infobuf,0,global_Context.infobuflen);
    
               DT_GetBitmapFromText(info,global_Context.infobuf,global_Context.mW,global_Context.mH,60);
               updateInfo(global_Context.infobuf, global_Context.infobuflen);
                int64_t time2 =getNowUs();
                ALOGD("take:%ld",time2-time1);
                drawFrame(global_Context.buf,global_Context.buflen);
                eglSwapBuffers(megldisplay, meglSurface);
                usleep(1000*1000/global_Context.mfps);
            }else{//循环播放
                fseek(fp,0,SEEK_SET);
            }
        }
        //销毁
        if(megldisplay!=NULL && meglSurface!=NULL && meglContext!=NULL){
            eglDestroySurface(megldisplay,meglSurface);
            eglDestroyContext(megldisplay,meglContext);
        }
        fclose(fp);
        free(global_Context.file_in);
        free(global_Context.buf);
        free(global_Context.infobuf);
        DT_deinit();
    #else
        initGles();
        ALOGD(" intglescontext ok ");
    
        createprograme();
        ALOGD(" createprograme ok ");
    
        global_Context.buflen=global_Context.mW*global_Context.mH*3/2;
        global_Context.buf = (unsigned char*)malloc( global_Context.buflen);
        if(global_Context.buf == NULL){
            ALOGE("err to malloc!");
            return NULL;
        }
    
        while(mbRun){
            //条件等待
            pthread_mutex_lock(&global_Context.mCondMutex);
            pthread_cond_wait(&global_Context.mCond,&global_Context.mCondMutex);
            pthread_mutex_unlock(&global_Context.mCondMutex);
    
    
            pthread_mutex_lock(&global_Context.bufMutex);
            do {//渲染,期间不允许同时修改
                drawFrame(global_Context.buf, global_Context.buflen);
                eglSwapBuffers(megldisplay, meglSurface);
            }while(0);
            pthread_mutex_unlock(&global_Context.bufMutex);
    
        }
        //销毁
        if(megldisplay!=NULL && meglSurface!=NULL && meglContext!=NULL){
            eglMakeCurrent(megldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            eglDestroySurface(megldisplay,meglSurface);
            eglDestroyContext(megldisplay,meglContext);
            eglTerminate(megldisplay);
        }
        free(global_Context.buf);
    #endif
        pthread_exit(NULL);
        return NULL;
    }
    void mygl_start(ANativeWindow *pWnid, char*file_in,int type, int w, int h,int fps){
        global_Context.file_in = file_in;
        global_Context.type = type;
        global_Context.nwin = pWnid;
        global_Context.mW = w;
        global_Context.mH = h;
        global_Context.mfps =fps;
        ALOGD("start run");
        //创建渲染程序:
        if(mbRun){
           ALOGE("had run, return");
        }
        mbRun =true;
        int ret =0;
        if(0 != (ret = pthread_create(&global_Context.mThread,NULL,renderthread,NULL))){
            ALOGE("pthread_create erro");
        }
        //pthread_detach(pd);
    }
    void mygl_render(unsigned char *buf,int size){
    
        pthread_mutex_lock(&global_Context.bufMutex);
        do {//拷贝,期间不允许同时修改
            memcpy(global_Context.buf,buf,MIN(size,global_Context.buflen));
        }while(0);
        pthread_mutex_unlock(&global_Context.bufMutex);
    
        //释放条件
        pthread_mutex_lock(&global_Context.mCondMutex);
        pthread_cond_signal(&global_Context.mCond);
        pthread_mutex_unlock(&global_Context.mCondMutex);
    
    }
    void mygl_stop(){
        mbRun = false;
        //释放条件
        pthread_mutex_lock(&global_Context.mCondMutex);
        pthread_cond_signal(&global_Context.mCond);
        pthread_mutex_unlock(&global_Context.mCondMutex);
    
        //等待结束,避免出现 FORTIFY: pthread_mutex_lock called on a destroyed mutex
        pthread_join(global_Context.mThread,NULL);
    }
    
    Openglfuncs.h
    #ifndef __OPENGLFUNCS_HH
    #define __OPENGLFUNCS_HH
    #include<jni.h>
    #include<android/native_window.h>
    #include<android/native_window_jni.h>
    #include <GLES2/gl2.h>
    #include <GLES2/gl2ext.h>
    #include<EGL/egl.h>
    #include<EGL/eglext.h>
    
    #define TYPE_YUV420P 19
    #define TYPE_YUV420SP_NV12 21
    void mygl_start(ANativeWindow *pWnid,char*file_in, int type,int w, int h,int fps);
    void mygl_stop();
    
    //下面函数供其它模块输入渲染数据
    void mygl_render(unsigned char *buf,int size);
    #endif
    Android.mk
    LOCAL_PATH := $(call my-dir)
    
    
    #这一部分是 用来预构建 freetype库的,freeType库可以从官方网站下载,利用其中的cmake编译,得到交叉编译的库,然后在这里 prebuilt
    include $(CLEAR_VARS)
    LOCAL_MODULE    := freetype
    LOCAL_SRC_FILES :=  ./my_libs/$(TARGET_ARCH_ABI)/libfreetyped.so
    
    MY_FREETYPE_SRCDIR=freeType/freetype-2.10.4.tar/freetype-2.10.4/
    MY_ABSULATION_DIR=$(LOCAL_PATH)/$(MY_FREETYPE_SRCDIR)
    LOCAL_EXPORT_C_INCLUDES := $(MY_ABSULATION_DIR)/  \
        $(MY_ABSULATION_DIR)/include/ \
    	$(MY_ABSULATION_DIR)/include/freetype/ \
    	$(MY_ABSULATION_DIR)/include/freetype/config/ \
    	$(MY_ABSULATION_DIR)/include/freetype/internal/
    
    include $(PREBUILT_SHARED_LIBRARY)
    
    
    
    include $(CLEAR_VARS)
    LOCAL_MODULE    := NativeOpengl
    LOCAL_SRC_FILES :=  Openglfuncs.cpp \
    					GlPragrame.cpp \
    					DrawText.cpp \
    					jni_NativeOpengl.cpp
    
    LOCAL_SHARED_LIBRARIES :=freetype
    LOCAL_LDLIBS += -lGLESv2 -lEGL \
    				-llog -landroid
    LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES
    
    include $(BUILD_SHARED_LIBRARY)
    
    

    展开全文
  • opengl显示文字

    千次阅读 2013-10-12 14:19:25
    在目前的项目中因为有涉及到OpenGL文字输出之类的操作,于是做了一个总结,并把一个库进行了改进,在这里贴出来,希望大家能够在将来的编程中省去一些写细节代码的时间,同时也是抛砖引玉。 首先,要注明的是唐明理...
  • OPENGL 显示文字 SharpGL+SharpFont+纹理

    千次阅读 2018-07-20 09:59:44
    Opengl作为一个专业的绘图技术,竟然没有显示文字的接口。后来经过详细研究,发现显示文字是一个非常高深的问题。Opengl作为一个底层API已经不适合提供对应的接口。 环境搭建 在开始之前,我们需要搭建开发环境。...
  • HelloWorld opengl文字显示

    千次阅读 2016-12-22 15:47:54
    转自网摘 源地址己找不到。没示例程序。 增加了两个文件,showline.c, showtext.c。分别为第二个和第三个示例程序的main函数相关部分。 在ctbuf.h和textarea.h最开头...第二个,带缓冲的显示文字。代码为:showlin
  • OPENGL 显示文字

    2012-11-13 20:45:11
    最近对opengl输出文字比较...在windows下wglUseFontBitmaps与wglUseFontOutlines提供opengl输出文字的字符资源,二者均通过构建一系列显示列表,然后用glCallLists执行这些显示列表就可以输出我们想要的字符。wglUseFo
  • void CGLFont::c3dtext(CString strText,HFONT hFont)// 3D文字显示,支持汉字。 { UCHAR * pChar=(UCHAR*)strText.GetBuffer(strText.GetLength());//定义字符串长度 int nListNum; //显示列表 D
  • OpenGL 调用FreeType2.0显示文字

    热门讨论 2013-01-08 13:38:54
    本程序是使用FreeType2.0库, 在OpenGL显示文字, 若字库文件支持unicode, 则可显示汉字等东亚字符, 本程序是本人为嵌入式系统所写的一个原型程序,若加上着色器,便可很容易移植到OPenGL ES2.0
  • OpenGL显示文字--显示英文 【转】

    千次阅读 2014-06-09 09:18:16
    OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。 各种流行的图形操作系统,例如Windows系统和Linux系统,都提供了一些功能,以便...
  • 在C#中,通过FreeType方式为OpenGL显示文字。 使用了SharpFont和SharpGL类库
  • Linux下Modern OpenGL显示汉字

    千次阅读 2017-08-24 14:48:14
    Linux下Modern OpenGL显示汉字前一篇中解码出来的图像已经能够正常颜色格式转换和显示了,现在遇到了新的问题:显示文字 本以为这是一个很简单的东西,因为之前使用SDL时直接应用sdl_ttf库很简单的就实现了文字渲染...
  • C# OpenGl 文字显示

    千次阅读 2014-08-07 08:02:49
    1. 显示列表 index = GL.glGenLists(96); GL.glListBase(index); var hFont = GL.CreateFont( 40, 0, 0, 0, GL.FW_DONTCARE, 0, 0, 0, GL.DEFAULT_CHARSET, G
  • OpenGL汉字显示

    2013-12-24 16:57:54
    OpenGL TrueType 汉字 显示
  • 转载:OpenGL显示文字

    2012-01-17 01:49:01
    转载:OpenGL显示文字 2010年07月03日  本课我们来谈谈如何显示文字。  OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能了。  ...
  • Android OpenGL 文本显示 LabelMaker

    千次阅读 2019-04-18 10:59:27
    AndroidApiDemos 里有个LabelMaker, 封装了OpenGl 的text lables的生产和显示,用起来非常方便: LabelMaker:OpenGL text labels通过创建Bitmap、将所有text labels绘制到Bitmap、将Bitmap转换为alpha texture ...
  • OpenGL显示三维汉字

    千次阅读 2015-01-08 18:54:06
    OpenGL显示三维汉字的控制台完整源码。在VS2013上可以运行。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,053
精华内容 5,221
关键字:

opengl显示文字