Android Freetype的学习过程总结

Freetype的学习过程总结

1. 编译环境的搭建
AndroidQ以前的版本编译freetype的库文件,需要设置ndk的编译环境,用make_standalone_toolchain.sh的脚本配置环境,但是现在google的官网上,已经抛弃了sh文本的使用,改用python的脚本,所以从官网上找到如下的配置环境。

1.1 64bits的编译环境配置如下:
python ~/androidQ/sdk_10.0/ndk-bundle/build/tools/make_standalone_toolchain.py --arch arm64 --api 28 --install-dir=my-toolchain –force

# Add the standalone toolchain to the search path.
export PATH=$PATH:`pwd`/my-toolchain/bin

# Tell configure what tools to use.
export target_host=aarch64-linux-android
export AR=$target_host-ar
export AS=$target_host-clang
export CC=$target_host-clang
export CXX=$target_host-clang++
export LD=$target_host-ld
export STRIP=$target_host-strip

# Tell configure what flags Android requires.
export CFLAGS="-fPIE -fPIC"
export LDFLAGS="-pie"

./configure --host= aarch64-linux-android --prefix=/freetype --without-zlib --with-png=no --with-harfbuzz=no
以上的配置需要做,因为编译出来的动态库,才会连接libc.so否则就会链接libc.so.6
Linux的应用链接的是libc.so.6
Android的应用链接的是libc.so

make -j10
make install DESTDIR=$(pwd)

1.2 32bits的编译环境配置如下:
区别64bits的编译配置的地方就是如下的操作,另外要设置:
export target_host= arm-linux-androideabi
./configure --host=arm-linux-androideabi --prefix=/freetype --without-zlib --with-png=no --with-harfbuzz=no
其他的没什么区别。

2. Android studio 的工程的编译以及遇到的问题
运行Android的工程遇到的问题总结:

  1. 如何解决Android studio错误“Unsupported Modules Detected: Compilation is not supported for following modules”

      1) 结束项目
      2) 关闭Android工作室
      3) 删除.IDEA目录
      4) 删除所有.iml文件
      5) 打开android studio并导入该项目
    
  2. 注意:AndroidP之后的版本,不允许,操作sdcard的内容,所以font.ttf的文件可以放到Android的application的安装目录下:

    File file = new  File(context.getFilesDir() + "/font.ttf");
    Log.d("Text", file.getPath() + "canread =" + file.exists());
    
  3. 由于Android的对三方的link的库文件有限制,需要把三方的库文件先添加到

    手机目录下的/system/etc/public.libraries.txt 文件中,然后再把libfreetype.so的文件分别push到 /system/lib和/system/lib64 的目录下,否则开机会找不到这个库文件,系统无法开机。并且程序运行也找不到对应的文件。

  4. Android studio的build.gradle编译脚本的配置:

        defaultConfig {
            applicationId 'com.android.gles3jni'
            minSdkVersion "${platformVersion}"
            targetSdkVersion 28
            ndk {
                abiFilters 'armeabi-v7a' //这边需要配置,否则默认的是windows_x86_64的编译器,然后跟arm平台的编译libfreetype的库不符合。
            }
        }
    	externalNativeBuild {
    	    cmake {
    	        path 'src/main/cpp/CMakeLists.txt'//编译native的库文件
    	    }
    	}
    
  5. Makefile的编写需要注意下:

    	cmake_minimum_required(VERSION 3.4.1) //这个是版本号
    			set(OPENGL_LIB GLESv3) // GLESv3这个应该是Android环境对OpenGL支持的宏定义
    			// 设置OPENGL_LIB为GLESv3
    			include_directories( //头文件放在根目录下会减少编译错误也好识别
    			        ./include/freetype2/freetype
    			        ./include/freetype2/
    			        ./)
    			
    			add_library(gles3jni SHARED
    			            gles3jni.cpp 
    			            RendererES2.cpp
    		            RendererES3.cpp)
    		# Include libraries needed for gles3jni lib
    		target_link_libraries(gles3jni
    		            ${OPENGL_LIB}
    		            android
    		            m
    		            EGL
    		            log
    		            -l${PROJECT_SOURCE_DIR}/libs/armeabi-v7a/libfreetype.so
    		        )// PROJECT_SOURCE_DIR是当前makefile文件的路径		
    

3. Freetype使用OpenglES绘制过程遇到的问题

		opengles 的glDrawArray 的调用参数说明
		float vertices[] = {
		        // positions           // texture coords
		        0.5f,  0.5f, 0.0f,   1.0f, 1.0f, // top right
		        0.5f, -0.5f, 0.0f,   1.0f, 0.0f, // bottom right
		        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f, // bottom left
		        -0.5f,  0.5f, 0.0f,  0.0f, 1.0f  // top left
		};
		
		unsigned int indices[] = {
		        2, 1, 0, // first triangle
		        2, 0, 3  // second triangle
		};

在这里插入图片描述
目前我的手机测试下来的结果是,opengl的绘制,以手机屏幕的中心点为原点(0,0)(现在只考虑2D的平面,没有projection的转换)调用glDrawArray的时候,以左下角的(-0.5f, -0.5f)为起点,按照逆时针的方向绘制顶点,用indices的设置需要是2,1,0的顺序,第二个三角形也是依次类推的做绘制的操作glUseProgram的函数调用需要设置在那个vertices data的配置之前,否则可能不生效。

4. freetype的字体查找绘制的过程
4.1 freetype的库文件,主要是以下的这三个,所以一版的功能只需要include以下的三个文件就行

#include <ft2build.h>
#include <ftglyph.h>
#include FT_FREETYPE_H

4.2 freetype的绘制流程如下:
4.2.1 opengl的绘制有时候输出的结果不是预期的时候,不好分析问题,一般先check三部分的内容,

	static const char VERTEX_SHADER[] =
	    "#version 300 es\n"
	    "layout (location = 0) in vec2 vertex;\n"
	    "layout (location = 1) in vec2 aTexCoord;\n"
	    "out highp vec2 TexCoords;\n"
	    "void main() {\n"
	    "    gl_Position = vec4(vertex, 0.0, 1.0);\n"
	    "    TexCoords = aTexCoord;\n"
	    "}\n";
	
	static const char FRAGMENT_SHADER[] =
	    "#version 300 es\n"
	    "in highp vec2 TexCoords;\n"
	    "out highp vec4 color;\n"
	    "uniform sampler2D text;\n"
	    "uniform highp vec3 textColor;\n"
	    "void main() {\n"
	    "    highp vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n"
	    "    color = vec4(textColor, 1.0) * sampled;\n"
	    //"    color = texture(text, TexCoords);\n"
	    //"    color = vec4(1.0, 0.0, 1.0, 1.0);\n" //debug使用
	    "}\n";
	a.	texture的问题,为了验证program和vertex的data没有问题,我先把以上的fragmeng的shader写上固定的颜色 
		color = vec4(1.0, 0.0, 1.0, 1.0),check是否正确的生成了texture的内容。
	b.	vertex 的问题,设置vertex的位置如下:
		float vertices11[] = {
		        // positions           // texture coords
		        0.5f,  0.5f, 0.0f,   1.0f, 1.0f, // top right
		        0.5f, -0.5f, 0.0f,   1.0f, 0.0f, // bottom right
		        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f, // bottom left
		        -0.5f,  0.5f, 0.0f,  0.0f, 1.0f  // top left
		};
		看下能否绘制出来最基本的两个三角形,也就是长方形的框。
	c.	需要check每一步gl的调用输出是否有error,这样就需要调用geterror来打印log分析哪一步有问题。
		bool checkGlError(const char* funcName) {
		    GLint err = glGetError();
		    if (err != GL_NO_ERROR) {
		        ALOGE("GL error after %s(): 0x%08x\n", funcName, err);
		        return true;
		    }
		    return false;
		}

4.2.2. 代码的实现部分:

		bool RendererES3::init() {
		    mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
		    if (!mProgram)
		        return false;
		
		    // Set OpenGL options
		    glEnable(GL_CULL_FACE);
		    glEnable(GL_BLEND);
		    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
		    glUseProgram(mProgram);
		    // FreeType
		    FT_Library ft;
		    // All functions return a value different than 0 whenever an error occurred
		    if (FT_Init_FreeType(&ft)) 
		        ALOGE("ERROR::FREETYPE: Could not init FreeType Library");
		
		    // Load font as face
		    FT_Face face;
		/*load新的字库,这边我用的是中文的字库做实验,如果无法读取这个ttf的字库会返回error*/
		    if (FT_New_Face(ft, "/data/data/com.android.gles3jni/DroidSansFallback.ttf", 0, &face))
		        ALOGE("ERROR::FREETYPE: Failed to load font");
		    // Set size to load glyphs as
		    FT_Set_Pixel_Sizes(face, 0, 48);
		
		    // Disable byte-alignment restriction
		    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		    checkGlError("1111111111111111");
		    // Load first 128 characters of ASCII set,如果是英文的字符ASCII码的话,就只需要存储128个字符对应的bitmap的信息,这样可能会节省时间方便查找绘制,如果是中文的字库的话,就不建议这么做,因为中文的字形状有很多种,需要另外的缓存机制,类似Android skia的缓存机制,需要研究下
		    //for (GLubyte c = 0; c < 128; c++)
		    //{wchar_t cc = 0x6211; “unicode的“我”字的编码”,因为freetype无法试别中文字体,需要转换成unicode的编码才能查到这个字体
		    {//wchar_t cc = 0x3002;//这个是个“。”的字形
		        // Load character glyph
		        int error = 0;
		        FT_Load_Char(face, 0x3002, FT_LOAD_RENDER);
		        {
		            ALOGE("ERROR::FREETYTPE: Failed to load Glyph");
		            //continue;
		        }
		       // error = FT_Get_Glyph(face->glyph, &glyph);
		 //目前实验下来,FT_Get_Char_Index+FT_Get_Glyph+ FT_Glyph_To_Bitmap,拿不到文字的bitmap,画出来是乱码,这个需要再查看接口使用是否有问题。不管是中文字形的unicode 和英文的ASCII都能正确的查询得到bitmap和基本的字形信息。
		        if(!error)
		        {
		           // FT_Glyph_To_Bitmap(&glyph,FT_RENDER_MODE_NORMAL, 0, 0);
		           // FT_BitmapGlyph    bitmap_glyph = (FT_BitmapGlyph)glyph;
		          //  FT_Bitmap      bitmap = bitmap_glyph->bitmap;
		
		        // Generate texture
		        GLuint texture;
		        glGenTextures(1, &texture);
		        glBindTexture(GL_TEXTURE_2D, texture);
		        glTexImage2D(
		                GL_TEXTURE_2D,
		                0,
		                GL_RED,//由于freetype默认的load的bitmap的图片是只有R通道的输出,所以只用这个RED的设置就行。以后的设置会有区别,会根据数据填充的方式不同变化。
		                face->glyph->bitmap.width,//bitmap的宽
		                face->glyph->bitmap.rows, //bitmap的高
		                0,
		                GL_RED,
		                GL_UNSIGNED_BYTE,
		                face->glyph->bitmap.buffer//bitmap的字形raw data
		        );
		
		        // Set texture options
		        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		        // Now store character for later use, 这边是存储查询到的信息,做成了map,这个是为了一开始实现ASCII做缓存用的
		        Character character = {
		                texture,
		                face->glyph->bitmap.width, face->glyph->bitmap.rows,
		                face->glyph->bitmap_left, face->glyph->bitmap_top,
		                face->glyph->advance.x
		        };
		        Characters.insert(std::pair<char , Character>('a', character));
		        }
		    }
		    checkGlError("2222222222222");
		    glBindTexture(GL_TEXTURE_2D, 0);
		    // Destroy FreeType once we're finished
		    FT_Done_Face(face); //查询完字体之后需要释放freetype的内存。
		    FT_Done_FreeType(ft);
		
		    checkGlError("3333333333333");
		    // Configure VAO/VBO for texture quads
		    glGenVertexArrays(1, &VAO);
		    glGenBuffers(1, &VBO);
		    glBindVertexArray(VAO);
		    glBindBuffer(GL_ARRAY_BUFFER, VBO);
		    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * 4, NULL, GL_DYNAMIC_DRAW); //初始化buffer data的信息
		    glEnableVertexAttribArray(0);
		    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); //初始化vertex data的信息
		    glBindBuffer(GL_ARRAY_BUFFER, 0);
		    glBindVertexArray(0);
		    ALOGE("Using OpenGL ES 3.0 renderer 111");
		    checkGlError("444444");
		    return true;
		}
		
		真正的绘制字体的部分,主要是计算vertex的位置信息:
		void RendererES3::RenderText(std::string  text, GLfloat x, GLfloat y, GLfloat scale, float color[])
		{
		    // Activate corresponding render state
		    glUniform3f(glGetUniformLocation(mProgram, "textColor"), color[0], color[1], color[2]);//这个可以做背景颜色的绘制,这个之后再更新
		    glActiveTexture(GL_TEXTURE0);
		    glBindVertexArray(VAO);
		
		    // Iterate through all characters
		    //std::string::const_iterator c;
		    GLfloat skip = 0.0;
		
		    //for (c = text.begin(); c != text.end(); c++)
		    {
		        Character ch = Characters['a'];
		        //ALOGE(" char : %c", *c);
		        ALOGE("x = %f, y=%f", x, y);
		        ALOGE("w = %u, h =%u", ch.w, ch.h);
		        ALOGE("bx = %d, by =%d", ch.BearingX, ch.BearingY);
		        ALOGE("Ad = %ld", ch.Advance);
		        GLfloat xpos = x + ch.BearingX *scale; //scale字体需要做放大处理,否则对应到屏幕上,会很小,但是目前无法确认这个bearingx的单位是什么,可能是1/64的pixel的大小。
		
		        xpos /= 1080.0f;
		        GLfloat ypos = y - (ch.h - ch.BearingY) ; //(x,y)是字体绘制左下角的起始位置
		        ypos /= 1920.0f;//因为我没有乘上投影矩阵,所以我这边需要除以宽高,做归一化的处理这个有待研究,是否需要除以宽高
		
		        GLfloat w = ch.w * scale / 1080.0f;
		        GLfloat h = ch.h * scale / 1920.0f;
		        //skip /= 1080.0f;
		        // Update VBO for each character 以下的设置是画在(0,0)周围
		        GLfloat vertices[4][4] = {
		                { (xpos + w)+skip, (ypos + h),   1.0, 1.0 },
		                { (xpos + w)+skip, -ypos,       1.0, 0.0 },
		                { -xpos+skip,     -ypos,       0.0, 0.0 },
		                { -xpos+skip,     (ypos + h),   0.0, 1.0 },
		        }; //这个贴图出来的文字是倒立的,所以需要texture的位置如下:
		GLfloat vertices[4][4] = {
		        { (xpos + w)+skip, (ypos + h),   1.0, 0.0 },
		        { (xpos + w)+skip, -ypos,       1.0, 1.0 },
		        { -xpos+skip,     -ypos,       0.0, 1.0 },
		        { -xpos+skip,     (ypos + h),   0.0, 0.0 },
		};其实就是图片的竖直方向的翻转可参考手机坐标屏幕的位置
		
		        // Render glyph texture over quad
		        glBindTexture(GL_TEXTURE_2D, ch.TextureID);
		        // Update content of VBO memory
		
		        glBindVertexArray(VAO);
		        glBindBuffer(GL_ARRAY_BUFFER, VBO);
		        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) *16, vertices, GL_DYNAMIC_DRAW);
		        //glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);      // glBufferSubData 和glBufferData 的使用需要再做check https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/07%20Advanced%20Data/
		        // position attribute
		        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
		        glEnableVertexAttribArray(0);
		        // texture coord attribute
		        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float) + 0));
		        glEnableVertexAttribArray(1);
		
		        glBindBuffer(GL_ARRAY_BUFFER, 0);
		        // Render quad
		        glDrawArrays(GL_TRIANGLES, 0, 3);
		        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indices);
		        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels) 画多个文字的时候需要有间隔,这个可做参考,但是我觉着还是用width做参考比较好,这个advance的值应该是用在上下文字距离的计算
		        skip = w ; // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
		    }
		    glBindVertexArray(0);
		    glBindTexture(GL_TEXTURE_2D, 0);
		}

以上运行application之后运行的结果如下:
在这里插入图片描述
5. Freetype显示特殊字体的设置
5.1 斜体设置
斜体在FreeType中可以通过矩阵变换来实现,只要把矩阵设置成一个切边矩阵就可以了,方法如下:

	// 倾斜度,越大就越斜
	float lean = 0.5f;
	FT_Matrix matrix;
	matrix.xx = 0x10000L;
	matrix.xy = lean * 0x10000L;
	matrix.yx = 0;
	matrix.yy = 0x10000L;
	FT_Set_Transform( face, &matrix, 0 );
	FT_Load_Char(face, 0x6211, FT_LOAD_RENDER);

5.2 描边设置,这个算法看起来比较复杂,这个是从网上找的范例

index = FT_Get_Char_Index(face, 0x6211); //从字库中找到这个中文字体的轮廓点阵
//load为一个不需要生成bitmap的glyph
if (FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP) == 0) {
    Pixel32 *pxl = NULL;
    GLuint imgWidth = 0; //生成的pixel的宽/高
    GLuint imgHeight = 0;
    GLuint imgSize = 0;
    ALOGE("DEBUG::FREETYTPE: load Glyph successfully");
    if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
        Spans spans;
        RenderSpans(ft, &face->glyph->outline, &spans);//拿到原始的轮廓点阵
        Spans outlineSpans;
        FT_Stroker stroker;
        FT_Stroker_New(ft, &stroker); //FT_Stroker_New创建一个笔触
//设置笔触为描边
        FT_Stroker_Set(stroker, (int)(WriteGlyphAsTGA * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);

        if (FT_Get_Glyph(face->glyph, &glyph) == 0) {
            FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
            if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
                FT_Outline *out = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
                RenderSpans(ft, out, &outlineSpans); //拿到描边的轮廓点阵
            }
            FT_Stroker_Done(stroker);
            FT_Done_Glyph(glyph);

            if (!spans.empty())
            {
                // Figure out what the bounding rect is for both the span lists.
计算出来最大的rect范围,包含所有的轮廓点。
                Rect rect(spans.front().x,
                          spans.front().y,
                          spans.front().x,
                          spans.front().y);
                for (Spans::iterator s = spans.begin();
                     s != spans.end(); ++s)
                {
                    rect.Include(Vec2(s->x, s->y));
                    rect.Include(Vec2(s->x + s->width - 1, s->y));
                }
                for (Spans::iterator s = outlineSpans.begin();
                     s != outlineSpans.end(); ++s)
                {
                    rect.Include(Vec2(s->x, s->y));
                    rect.Include(Vec2(s->x + s->width - 1, s->y));
                }

                // Get some metrics of our image.
                 imgWidth = rect.Width(),
                        imgHeight = rect.Height(),
                        imgSize = imgWidth * imgHeight;

                // Allocate data for our image and clear it out to transparent.
                Pixel32 *pxl = new Pixel32[imgSize];
                memset(pxl, 0, sizeof(Pixel32) * imgSize);
                Pixel32 outlineCol = Pixel32(255, 90, 30);
                Pixel32 fontCol = Pixel32(255, 255, 255);
                // Loop over the outline spans and just draw them into the
                // image. 根据对应的轮廓的点阵,填充颜色,有点的地方才需要填充颜           色,这个需要研究如何计算这个点。
                for (Spans::iterator s = outlineSpans.begin();
                     s != outlineSpans.end(); ++s) 
                    for (int w = 0; w < s->width; ++w)
                        pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
                                  + s->x - rect.xmin + w)] =
                                Pixel32(outlineCol.r, outlineCol.g, outlineCol.b,
                                        s->coverage);

              // Then loop over the regular glyph spans and blend them into
             // the image.

                for (Spans::iterator s = spans.begin();
                     s != spans.end(); ++s)
                    for (int w = 0; w < s->width; ++w)
                    {
                        Pixel32 &dst =
                                pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
                                          + s->x - rect.xmin + w)];
                        Pixel32 src = Pixel32(fontCol.r, fontCol.g, fontCol.b,
                                              s->coverage);
                        dst.r = (int)(dst.r + ((src.r - dst.r) * src.a) / 255.0f);
                        dst.g = (int)(dst.g + ((src.g - dst.g) * src.a) / 255.0f);
                        dst.b = (int)(dst.b + ((src.b - dst.b) * src.a) / 255.0f);
                        dst.a = MIN(255, dst.a + src.a);
                    }
                // Generate texture
                GLuint texture;
                glGenTextures(1, &texture);
                glBindTexture(GL_TEXTURE_2D, texture);
                glTexImage2D(
                        GL_TEXTURE_2D,
                        0,
                        GL_RGBA, //上边的pixels的数据,是32位的数据,所以这边要添加RGBA的数据类型,否则会画不出来形状。
                        imgWidth,
                        imgHeight,
                        0,
                        GL_RGBA,
                        GL_UNSIGNED_BYTE,
                        pxl
                );

                // Set texture options
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                // Now store character for later use
                Character character = {
                        texture,
                        imgWidth, imgHeight,
                        10.0f, 10.0,
                        0
                };
                Characters.insert(std::pair<char , Character>('a', character));
            }
        }
    }
}

5.2.1 FT_outline的轮廓输出说明,

struct Span
{
  Span() { }
  Span(int _x, int _y, int _width, int _coverage)
  : x(_x), y(_y), width(_width), coverage(_coverage) { }

  int x, y, width, coverage;
};

(x,y)的坐标表示文字的轮廓落在了这个坐标点上,需要给这个点对应的显示颜色。
Y轴的数据从小开始,相同的y对应的整个横线上,一起存完,才会接着存y+1对应的坐标点。
X轴上,以bearingX为基线从小到大存贮span的点,
相同的x的坐标上,连续的坐标值,如果alpha的值一样的话,比如 3,4,5,6 有相同的alpha = 255的话,x = 3, w = 4, coverage = 255, y值的话就是纵坐标的值。
跑了句号“。”显示的坐标如下:(下图只是坐标的展示,没有那么准确)
在这里插入图片描述

	BearingX : 1, BearingY : 8 // 这个BearingY值需要研究?????
	整体的字体宽高是14,14
	
	x  :  y  :  spans[i].len  :  spans[i].coverage
	count = 6, y  =-6   //y的值可是负值,对应的-6的横线上,有6点,每个点的alpha都不同,所以需要存6个坐标点。
	 5  :  -6 :  1  : 22
	 6  :  -6 :  1  : 82
	 7  :  -6 :  1  : 105
	 8  :  -6 :  1  : 92
	 9  :  -6 :  1  : 40
	 10  :  -6 :  1  : 0
	……………
	count = 6, y  =2 // y=2的横线上,存储了8个点,其中x=2, 3的值,存储的alpha相同。
	1  :  2 :  1  : 170
	2  :  2 :  2  : 255
	4  :  2 :  1  : 113
	10  :  2 :  1  : 61
	11  :  2 :  2  : 255
	13  :  2 :  1  : 217
	轮廓就是一个一个的整数的坐标点组成的,可以参考下图理解,只画了部分边上的点,中间取整数省略了。

5.3 加粗
字体加粗的方式,freetype的中有

FT_Outline_Embolden(&face->glyph->outline,200)
FT_Outline_EmboldenXY(&face->glyph->outline, 1, 200);

我设置的是对轮廓加粗,xy的方法是,为了调整xy大小,实现不同程度的粗细的字体。

6. 设置texture的时候需要注意下:
生成纹理用glTexImage2D来生成:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);

参考了别的博客写的接口解释:
第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGBA值,因此我们也把纹理储存为RGBA值。
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
下个参数应该总是被设为0(历史遗留问题)。
第七第八个参数定义了源图的格式和数据类型。我们使用RGBA值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
最后一个参数是真正的图像,这个是存贮的数据pixels,如果这个里面保存的是RGBA的,格式,那
image[0] -> R值, 也就是char型的长度
image[1] -> G
image[2] -> B
image[3] -> A

当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。然而,目前只有基本级别(Base-level)的纹理图像被加载了,如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值