AndroidQ 圖形系統(6)使用純native API畫一個窗口

本篇文章根據前面的分析來實際操作畫一個窗口,使用純native API來實現,這裏的native API並非ndk,而是native層函數,需要在AOSP編譯環境下進行。

首先在/frameworks/native/libs/gui/目錄下創建drawWindowTest測試目錄,drawWindowTest目錄中創建Android.bpDrawWindowTest.cpp
在這裏插入圖片描述
Android.bp:

cc_binary {
    name: "drawWindow",
    srcs: ["DrawWindowTest.cpp"],
    shared_libs: [
        "liblog",
        "libbinder",
        "libgui",
        "libui",
        "libutils",
    ],
}

接着來看看DrawWindowTest.cpp怎麼寫,前面幾篇文章的分析我們大致瞭解了畫一個窗口的流程:

  1. 首先需要和SurfaceFlinger進程建立連接
  2. 創建圖形數據的載體Surface對應SurfaceFlinger進程Layer
  3. 有了Surface,還需要將Surface連接到BufferQueue
  4. 設置Surface的各種數據,如寬高,格式,Z-order,位置,縮放等
  5. 調用dequeueBuffer獲取buffer
  6. 向buffer中填充圖形數據
  7. 調用queueBuffer將buffer送到SurfaceFlinger合成

我們的代碼就按上面步驟來寫,DrawWindowTest.cpp源碼如下:

#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <system/window.h>
#include <ui/GraphicBuffer.h>
#include <ui/Fence.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <gui/SurfaceControl.h>
#include <gui/SurfaceComposerClient.h>
#include <binder/IBinder.h>
#include <ui/DisplayInfo.h>
#include <gui/Surface.h>
using namespace android;
//狀態值
status_t err;

sp<SurfaceComposerClient> mSurfaceComposerClient;

sp<SurfaceControl> mSurfaceControl;
//屏幕寬高
int mWidth,mHeight;
//GraphicBuffer的父類ANativeWindowBuffer
ANativeWindowBuffer *mNativeBuffer = nullptr;
//連接SurfaceFlinger
void connectSurfaceFlinger();
//獲取Surface
sp<ANativeWindow> getSurface();
//連接BufferQueue
void connectBufferQueue(ANativeWindow *surface);
//設置Transaction
void setBufferTransaction();
//設置其他信息
void setBuffer(ANativeWindow *surface);

int main() {
    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    //連接SurfaceFlinger
    connectSurfaceFlinger();
    //獲取surface
    ANativeWindow *surface = getSurface().get();
    //surface連接bufferqueue
    connectBufferQueue(surface);
    //設置Transaction
    setBufferTransaction();
    //設置其他信息
    setBuffer(surface);

    int fenceFD= -1;
    //申請buffer
    err = surface->dequeueBuffer(surface, &mNativeBuffer, &fenceFD);
    if (err != NO_ERROR) {
            ALOGE("dequeueBuffer err....");
        }
        //通過Fence確認申請的buffer是否完全被上一個使用者使用完
        sp<Fence> fence(new Fence(fenceFD));
        //等待收到releaseFence
        int waitResult = fence->waitForever("dequeueBuffer_EmptyNative");
        if (waitResult != OK) {
           ALOGE("Fence wait err....");
        }
        //ANativeWindowBuffer轉GraphicBuffer
        sp<GraphicBuffer> buff(GraphicBuffer::from(mNativeBuffer));
        //buffer數據
        uint8_t *data = NULL;
        //通過lock先鎖住buffer
        err = buff->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&data));
        if (err != NO_ERROR) {
            ALOGE("lock buffer err....");
        }

        //填充數據,這裏直接賦值爲0,會畫一個黑色窗口
        *data = 0;
        err = buff->unlock();
        if (err != NO_ERROR) {
            ALOGE("unlock buffer err....");
        }

        //將填充好的buffer送到SurfaceFlinger進行合成顯示
        err = surface->queueBuffer(surface, buff->getNativeBuffer(), -1);
        if (err != NO_ERROR) {
            ALOGE("queueBuffer buffer err....");
            return err;
        }
    mNativeBuffer = NULL;
    IPCThreadState::self()->joinThreadPool();
    return EXIT_SUCCESS;
}

//1.創建SurfaceComposerClient,這是SurfaceFlinger的Client端
void connectSurfaceFlinger(){
    
    mSurfaceComposerClient = new SurfaceComposerClient;
    err = mSurfaceComposerClient->initCheck();
    if (err != NO_ERROR) {
        ALOGE("SurfaceComposerClient initCheck err....");
        return;
    }
}
//2.創建Surface,ANativeWindow是創建Surface父類
sp<ANativeWindow> getSurface(){
    sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
    android::DisplayInfo mainDisplayInfo;
    //獲取手機的屏幕信息
    err = SurfaceComposerClient::getDisplayInfo(display, &mainDisplayInfo);
    if (err != NO_ERROR) {
        ALOGE("getDisplayInfo err....");
    }
    //屏幕寬
    mWidth = mainDisplayInfo.w;
    //屏幕高
    mHeight = mainDisplayInfo.h;
    //創建surface對應surfaceflinger進程layer
    mSurfaceControl = mSurfaceComposerClient->createSurface(
            String8("drawWindow"), mWidth/2, mHeight/2,
            PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
    if (mSurfaceControl == NULL || !mSurfaceControl->isValid()) {
        ALOGE("mSurfaceControl err....");
    }
    //獲取surface
    sp<ANativeWindow> anw = mSurfaceControl->getSurface();
    return anw;
}
//3.Surface連接BufferQueue
void connectBufferQueue(ANativeWindow *surface){
    err = native_window_api_connect(surface, NATIVE_WINDOW_API_CPU);
    if (err != NO_ERROR) {
        ALOGE("connect bufferqueue err....");
    }
}
//4.設置buffer數據
void setBufferTransaction(){
    /*
    setLayer():設置窗口的Z-order
    setPosition():設置窗口顯示的位置
    show():設置窗口顯示出來
    apply():將設置的窗口信息應用到SurfaceFlinger,真正生效
    */
    SurfaceComposerClient::Transaction{}
            .setLayer(mSurfaceControl, 0x7FFFFFFF)
            .setPosition(mSurfaceControl,mWidth/4,mHeight/4)
            .show(mSurfaceControl)
            .apply();
}
//5.設置buffer
void setBuffer(ANativeWindow *surface){
    //設置usage
    err = native_window_set_usage(surface, GRALLOC_USAGE_SW_WRITE_OFTEN);
    if (err != NO_ERROR) {
	ALOGE("native_window_set_usage err....");	
    }
    //設置transform
    err = native_window_set_buffers_transform(surface, NATIVE_WINDOW_TRANSFORM_ROT_90);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_buffers_transform err....");
    }
    //設置scaling_mode
    err = native_window_set_scaling_mode(
            surface, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err != NO_ERROR) {
         ALOGE("native_window_set_scaling_mode err....");
    }
}

上面的代碼基本上是按照畫窗口的流程來實現的,看下上面setBuffer函數,裏面調用了一系列native_window_set_XXX函數來個Surface設置各種信息,其實最終都是調用到SurfacesetXXX函數,native_window_set_XXX是給外部調用提供操作Surface的接口,這些函數定義在frameworks/native/libs/nativewindow/include/system/window.h中,舉個例子看看native_window_set_usage函數

static inline int native_window_set_usage(struct ANativeWindow* window, uint64_t usage) {
    return window->perform(window, NATIVE_WINDOW_SET_USAGE64, usage);
}

會調用ANativeWindowperform函數:

int     (*perform)(struct ANativeWindow* window,
                int operation, ... );

這個函數指針是在Surface構造函數中賦值的:


ANativeWindow::perform          = hook_perform;
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
    va_list args;
    va_start(args, operation);
    Surface* c = getSelf(window);
    int result = c->perform(operation, args);
    va_end(args);
    return result;
}

hook_perform函數又調用Surfaceperform函數:

int Surface::perform(int operation, va_list args)
{
    int res = NO_ERROR;
    switch (operation) {
    case NATIVE_WINDOW_CONNECT:
        // deprecated. must return NO_ERROR.
        break;
    case NATIVE_WINDOW_DISCONNECT:
        // deprecated. must return NO_ERROR.
        break;
    case NATIVE_WINDOW_SET_USAGE:
        res = dispatchSetUsage(args);
        break;
    case NATIVE_WINDOW_SET_CROP:
        res = dispatchSetCrop(args);
        break;
    case NATIVE_WINDOW_SET_BUFFER_COUNT:
        res = dispatchSetBufferCount(args);
        .......
   }

這裏面有很多函數,根據傳遞過來的參數調用不同的dispatchXXX,比如native_window_set_usage傳遞的就是NATIVE_WINDOW_SET_USAGE64,從而調用dispatchSetUsage64

case NATIVE_WINDOW_SET_USAGE64:
        res = dispatchSetUsage64(args);
        break;

dispatchSetUsage64又調用setUsage最終將數據設置給了Surface

int Surface::dispatchSetUsage64(va_list args) {
    uint64_t usage = va_arg(args, uint64_t);
    return setUsage(usage);
}

畫一個窗口有很多信息必須設置,在之前分析的dequeueBufferqueueBuffer中都有相關判斷,比如寬高不能小於0,必須連接BufferQueue等。

編譯測試程序:mmm frameworks/native/libs/gui/drawWindowTest/
在這裏插入圖片描述
push進手機:
adb push out/target/product/SEOUL_ATT/system/bin/drawWindow /system/bin

運行這個測試程序:adb shell /system/bin/drawWindow
效果如下:
在這裏插入圖片描述
通過這一個測試程序結合前幾篇理論的文章可以更好的熟悉Android圖形架構。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章