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图形架构。

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值