AndroidR上普通的字體繪制在HWUI的部分實作
這里先說明下HWUI的部分呼叫OpenglES的流程,之后再更新博客說明上層的TextView的具體重繪程序,
1. 簡單的數字繪制的apk
寫了一個最簡單的例子,繪制數字和特殊字符的程序,作為研究HWUI的文字繪制的范例,
1.1 布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_hello_jnicallback"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hellojnicallback.MainActivity"
>
<TextView
android:id="@+id/tickView"
android:layout_width="300dp"
android:layout_height="200dp"
android:text="00:00:00"
android:background="@mipmap/ic_launcher"/>
</androidx.constraintlayout.widget.ConstraintLayout>
1.2 代碼的實作更新數字的程序
TextView tickView = (TextView) findViewById(R.id.tickView);
tickView.getPaint().setFakeBoldText(true);
tickView.getPaint().setSubpixelText(false);
tickView.setTextSize(60);
Code中只是簡單地獲取了時鐘的結果,然后把資料填給tickView
2. 從gapid的call trace 上分析如下:
2.1 畫面上關于臟資料區域的計算
螢屏臟區域的計算,一般都是求所有RendorNodes的dirty Region或的結果,我這個應用寫的簡單,是全屏顯示,并且只有一個main activity,所以只有一層layer,并且只更新了時鐘顯示的部分,區域(0, 240, 900, 840),
前提是GPU這邊要支持partial update的特性,否則這個dirty region的區域就是全屏的引數,
HWUI中等RenderThread通過OpenglES繪制操作都做完的時候,就會呼叫eglSwapBuffersWithDamageKHR,告訴GPU開始真正的繪制的操作,在GAPID中每個Frame Number:第一幀的開頭就會掉這個函式,
如下的log是我的這個應用的顯示更新的區域:
M03C914 09-12 02:02:50.654 9413 9490 E OpenGLRenderer:eglSwapBuffersWithDamageKHR, rect[0 ,1080, 900, 600]
HWUI以左上角為頂點(0,0)開始計算臟區域,轉換成GPU繪制區域需要做計算,這個計算在Android code的frameworks/base/libs/hwui目錄Frame.h中,這個應該是實際螢屏的區域,
2.2 HWUI中文字的繪制
HWUI對文字的繪制會預先生成的一張2048x1024的texture,作為upload文字的時候用,我之后會再補充HWUI中從上層apk到skia如何更新文字的,
glBindTexture 先系結這張texture
glPixelStorei GL_UNPACK_ALIGNMENT 1 設定一個位元組對齊
glPixelStorei GL_UNPACK_ROW_LENGTH 512 設定的width
glTexSubImage2D(GL_TEXTURE_2D, 0, 308(xoffset), 45(yoffset), 92(w), 130(h), GL_RED(format), GL_UNSIGNED_BYTE(型別), data(包含文字的raw 圖片內容))
現在上傳一張包含數字比如 “2” 的圖片,把它貼到從左上角開始的坐標(308, 45)+w:h(92,130)的區域,這張texture就是GPU的記憶體里面的圖片,
有時候,skia這邊會打包四個或者是多個文字一起,但是這幾個字是放在一個放行的圖片里面,一起upload到gpu里面的,
glPixelStorei GL_UNPACK_ALIGNMENT 0 清除設定一個位元組對齊
glBindFramebuffer(GL_FRAMEBUFFER, 0) 接下來的這些draw,是要繪制到主context上,也就
是主螢屏的framebuffer里面
glDrawArrays 如果這一幀里面的gldraw沒有呼叫glUseProgram的話,OpenGL就默認的使用前一個draw里面系結的program來做繪制的操作,(這個是gapid里面可以實驗一下)
2.3 從GAPID上截圖說明下postion 和color的設定
有時候這些資料看不到,可能GAPID有資料受限制,
先說明下,vertex + color + tex坐標的資料存盤,

如下圖1. 的部分是pos的設定,頂點4組資料如下,資料型別是float32的型別,否則看不出來:黑色括號中,就是對應的“0” “:” “0” “:” “2”五個字符的postion的坐標位置,float32的的格式,每個position需要占用8個Bytes,GAPID中每行是16個Bytes

如圖2. 的部分是color vec4()的設定,但是這個部分是一個4bytes的資料,上層設定的如下:tickView.setTextColor(Color.argb(128, 255, 1, 1));

括號內對應的是RGBA的資料,但是texture的資料,HWUI對這個RGBA的資料,255的不知道為什么寫成0x80,這個需要再check,
如圖3:shader中sin mediump uvec2 inTextureCoords;
就是設定texture的坐標入括號中表示,GAPID中只有int16的顯示,沒有short的顯示,只能自己做換算,

2.2 繪制的shader和program,texture的shader是跟文字屬性相關
vertex shader:
#version 320 es
precision mediump float;
precision mediump sampler2D;
uniform highp vec4 sk_RTAdjust;
uniform highp vec2 uAtlasDimensionsInv_Stage0;
in highp vec2 inPosition;
in mediump vec4 inColor;
in mediump uvec2 inTextureCoords;
out mediump vec4 vinColor_Stage0;
out highp vec2 vTextureCoords_Stage0;
flat out highp int vTexIndex_Stage0;
out highp vec2 vIntTextureCoords_Stage0;
void main() {
vinColor_Stage0 = inColor;
highp ivec2 signedCoords = ivec2(int(inTextureCoords.x), int(inTextureCoords.y));
highp vec2 unormTexCoords = vec2(float(signedCoords.x / 2), float(signedCoords.y / 2));
vTextureCoords_Stage0 = unormTexCoords * uAtlasDimensionsInv_Stage0;
vTexIndex_Stage0 = 0;
vIntTextureCoords_Stage0 = unormTexCoords;
gl_Position = vec4(inPosition.x, inPosition.y, 0.0, 1.0);
gl_Position = vec4(gl_Position.xy * sk_RTAdjust.xz + gl_Position.ww * sk_RTAdjust.yw, 0.0, gl_Position.w);
}
fragment shader:
#version 320 es
precision mediump float;
precision mediump sampler2D;
out mediump vec4 sk_FragColor;
uniform mediump float uDistanceAdjust_Stage0;
uniform sampler2D uTextureSampler_0_Stage0;
in mediump vec4 vinColor_Stage0;
in highp vec2 vTextureCoords_Stage0;
flat in highp int vTexIndex_Stage0;
in highp vec2 vIntTextureCoords_Stage0;
void main() {
mediump vec4 outputColor_Stage0;
mediump vec4 outputCoverage_Stage0;
{
outputColor_Stage0 = vinColor_Stage0;
highp vec2 uv = vTextureCoords_Stage0;
mediump vec4 texColor;
{
texColor = texture(uTextureSampler_0_Stage0, uv);
}
//猜測下面的這些處理,應該是在做gamma的矯正
mediump float distance = 7.96875 * (texColor.x - 0.50196081399917603);
distance -= uDistanceAdjust_Stage0;
mediump float afwidth;
afwidth = abs(0.64999997615814209 * dFdx(vIntTextureCoords_Stage0.x));
mediump float val = smoothstep(-afwidth, afwidth, distance);
outputCoverage_Stage0 = vec4(val);
}
{
sk_FragColor = outputColor_Stage0 * outputCoverage_Stage0;
}
}
之前在Android9.0上,在skia pipeline中,模仿opengl pipeline中做gamma矯正,修改shader如下:
注意:XXXXXXXXXXXXXXXXXXXXXXXXXXXX
編輯這個shader遇到的坑,
不認識#define,
不認識half3(4.6f, xxx)中f,只能寫4.6,
不認識vec3
#version 320 es
uniform lowp sampler2D uTextureSampler_0_Stage0;
in float2 vTextureCoords_Stage0;
flat in int vTexIndex_Stage0;
in half4 vinColor_Stage0;
out half4 sk_FragColor;
void main() {
half4 outputColor_Stage0;
half4 outputCoverage_Stage0;
{ // Stage 0, Texture
outputColor_Stage0 = vinColor_Stage0; //參考了programCache中gamma相關設定,
half4 texColor;
{
texColor = texture(uTextureSampler_0_Stage0, vTextureCoords_Stage0);
}
float luminance = dot(vinColor_Stage0.rgb, half3(0.2126, 0.7152, 0.0722)); //gamma 相關的計算, 調整這個half3(0.2126, 0.7152, 0.0722)中引數,為half3(0.7152, 0.2126, 0.0722)看效果沒什么變化
outputColor_Stage0 = outputColor_Stage0 * pow(texColor.a, luminance < 0.5 ? 0.69 : 1.45); // gamma相關的計算,但是這個1.45的話根據網路教程,調整到2.2的時候,會讓灰色的字體變得非常不清楚,不知道為什么??????
outputCoverage_Stage0 = half4(1);
}
{ // Xfer Processor: Porter Duff
sk_FragColor = outputColor_Stage0 * outputCoverage_Stage0;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/234924.html
標籤:其他
