View体系7:Canvas

2016-11-15  本文已影响192人  81bad73e9053

1.Surface.lockCanvas

draw(Canvas canvas)

与View组件打交道的是Canvas而不是Surface,但是应用进程与SurfaceFlinger的数据中介并不是Canvas而是Surface,这就不可避免的产生了一个问题:Canvas和Surface之间如何协作?ViewRootImpl中取得一个Canvas的方法如下:

canvas = mSurface.lockCanvas(dirty); 

1.1 lockCanvas

public Canvas lockCanvas(Rect inOutDirty)throws Surface.OutOfResourcesException, IllegalArgumentException {
    synchronized (mLock) {
        checkNotReleasedLocked();
        if (mLockedObject != 0) { 
            throw new IllegalStateException("Surface was already locked");
        }
        mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
        return mCanvas;
    }
} 

private static native int nativeLockCanvas(int nativeObject, Canvas canvas, Rect dirty)
        throws OutOfResourcesException; 

1.2 nativeLockCanvas

 //clazz对应java层的Surface
static jint nativeLockCanvas(JNIEnv* env, jclass clazz,
        jint nativeObject, jobject canvasObj, jobject dirtyRectObj) {
    //nativeObject对应C++层的Surface
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    //计算dirtyRegion 
    Rect dirtyRect;
    Rect* dirtyRectPtr = NULL;

    if (dirtyRectObj) {
        dirtyRect.left   = env->GetIntField(dirtyRectObj, gRectClassInfo.left);
        dirtyRect.top    = env->GetIntField(dirtyRectObj, gRectClassInfo.top);
        dirtyRect.right  = env->GetIntField(dirtyRectObj, gRectClassInfo.right);
        dirtyRect.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom);
        dirtyRectPtr = &dirtyRect;
    }

    ANativeWindow_Buffer outBuffer;//用于存储UI数据的Buffer
    //通过lock方法为outBuffer赋值
    status_t err = surface->lock(&outBuffer, dirtyRectPtr); 

    SkBitmap bitmap;
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
    
    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
        bitmap.setIsOpaque(true);
    }
    if (outBuffer.width > 0 && outBuffer.height > 0) {
        bitmap.setPixels(outBuffer.bits);//为Bitmap分配可用的数据空间
    } else { 
        bitmap.setPixels(NULL);
    }
    //通过bitmap构造本地canvas对象
    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
    //将本地canvas对象保存到java层canvas的数据成员变量中
    swapCanvasPtr(env, canvasObj, nativeCanvas);
 
    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    return (int) lockedSurface.get();
}

1.3

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
    if (mLockedBuffer != 0) {
        ALOGE("Surface::lock failed, already locked");
        return INVALID_OPERATION;
    }

    if (!mConnectedToCpu) {//step2判断是否已经建立必要的链接
        int err = Surface::connect(NATIVE_WINDOW_API_CPU); 
        //设置内存块的用法
        setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
    }

    ANativeWindowBuffer* out;
    int fenceFd = -1;
    //step5:buffer队列中dequeue一个可用的buffer
    status_t err = dequeueBuffer(&out, &fenceFd); 
    if (err == NO_ERROR) {
        //当前要处理的buffer
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        sp<Fence> fence(new Fence(fenceFd)); 
        err = fence->waitForever("Surface::lock"); 
        const Rect bounds(backBuffer->width, backBuffer->height);

        Region newDirtyRegion;
        //上一个buffer
        const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
        //是否可以从上一次操作中直接copy数据
        const bool canCopyBack = (frontBuffer != 0 &&
                backBuffer->width  == frontBuffer->width &&
                backBuffer->height == frontBuffer->height &&
                backBuffer->format == frontBuffer->format);

        if (canCopyBack) { 
            //计算可以从上一次的buffer中copy多少数据
            const Region copyback(mDirtyRegion.subtract(newDirtyRegion)); 
            if (!copyback.isEmpty())
                copyBlt(backBuffer, frontBuffer, copyback);
        } else { 
            newDirtyRegion.set(bounds);
            mDirtyRegion.clear(); 
            for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) {
                mSlots[i].dirtyRegion.clear();
            }
        }


    

        void* vaddr;
        //锁定buffer
        status_t res = backBuffer->lock(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr); 

        if (res != 0) {
            err = INVALID_OPERATION;
        } else {//收尾工作
            mLockedBuffer = backBuffer;
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            outBuffer->bits   = vaddr;
        }
    }
    return err;
} 

当前被lock的buffer最多只能有一个,以mLockedBuffer来表示,这是一个GraphicBuffer类型的强指针变量,每次lock成功之后,它就会被赋值为当前被lock的buffer,而当UI绘图结束并调用unlockAndPost时,这个mLockedBuffer会被清空且另一个变量mPostedBuffer用于记录最近一次post操作,因此在函数的开始需要判断mLockedBuffer是否为空,如果不是就要避免再次锁定一次buffer,直接返回错误。

1.4 native_window.h

typedef struct ANativeWindow_Buffer {
    // 宽度,以像素为单位
    int32_t width; 
    // 高度,以像素为单位
    int32_t height; 
    //内存中buffer每一行所占的像素值
    int32_t stride; 
    // 缓冲区格式
    int32_t format; 
    //存储数据的地方
    void* bits; 
    // 保留
    uint32_t reserved[6];
} ANativeWindow_Buffer;

2.unlockCanvasAndPost

UI绘图完成后,程序需要将这幅画解锁,并提交给SurfaceFlinger进行渲染,在performDraw完成之后,用unlockCanvasAndPost接口来告知Surface一个完整的绘图操作已经完成。

2.1 nativeUnlockCanvasAndPost

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jint nativeObject, jobject canvasObj) {
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
   
    // detach the canvas from the surface
    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
    swapCanvasPtr(env, canvasObj, nativeCanvas);
 
    status_t err = surface->unlockAndPost(); 
}

2.2 unlockAndPost

status_t Surface::unlockAndPost()
{
    if (mLockedBuffer == 0) { //当前是否有可用的buffer
        return INVALID_OPERATION;
    }
    //buffer解除锁定
    status_t err = mLockedBuffer->unlock(); 
    //buffer入队
    err = queueBuffer(mLockedBuffer.get(), -1);
    //用变量mPostedBuffer记录刚刚操作过的buffer
    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = 0;//mLockedBuffer清空,保证下一次lock的时候可以正常执行
    return err;
}

上一篇下一篇

猜你喜欢

热点阅读