软件绘制

软件绘制

深入理解Window

Android的UI显示原理之Surface的创建

Android的UI显示原理之Surface的渲染

https://github.com/SusionSuc/AdvancedAndroid/blob/master/AndroidFramework%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/Android%E8%A7%86%E5%9B%BE%E5%B1%82%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/Android%E7%9A%84UI%E6%98%BE%E7%A4%BA%E5%8E%9F%E7%90%86%E6%80%BB%E7%BB%93.md

https://github.com/SusionSuc/AdvancedAndroid/blob/master/framework/Android%E8%A7%86%E5%9B%BE%E5%B1%82%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/README.md

Android图形系统(九)-View、Canvas与Surface的关系


整体流程

把整个流程再简单总结下,View、Canvas与Surface的关系也就一目了然了:

Surface通过dequeueBuffer流程(具体操作在此不多赘述)获取一块存放绘制数据的buffer。

View 在onDraw的时候,通过传入的Canvas进行绘制。(这里只是一个绘制的入口而已,本文是针对requestLayout 流程来讲述的,当然你单独用Canvas实现绘制也是一样的)。

调用java层的CanvasAPI,实际真正负责绘制工作的是底层的Skia引擎,这里核心类包括SKCanvas(画家)以及SKBitmap(画布),绘制好的内容放入Surface 通过dequeueBuffer获取到的GraphicBuffer。

绘制完毕后,Surface通过queueBuffer将存放好绘制数据的buffer投递到队列中,并通知SurfaceFlinger消费。


SurfaceFlinger可以说是Android UI渲染体系的核心,在Android系统启动时会启动SurfaceFlinger服务,它的主要作用就是被Android应用程序调用,把绘制(测量,布局,绘制)后的窗口(Surface)渲染到手机屏幕上

image

SurfaceControl

image

image

surface.lockCanvas():

image

image

//android_view_Surface.cpp
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
       jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
   sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    ...
   ANativeWindow_Buffer outBuffer;
    //调用了Surface的dequeueBuffer,从SurfaceFlinger中申请内存GraphicBuffer,这个buffer是用来传递绘制的元数据的
   status_t err = surface->lock(&outBuffer, dirtyRectPtr);
   ...
   SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                        convertPixelFormat(outBuffer.format),
                                        outBuffer.format == PIXEL_FORMAT_RGBX_8888 ?
                                        kOpaque_SkAlphaType : kPremul_SkAlphaType);
   //新建了一个SkBitmap,并进行了一系列设置
   SkBitmap bitmap;
   ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
   bitmap.setInfo(info, bpr);
   if (outBuffer.width > 0 && outBuffer.height > 0) {
       bitmap.setPixels(outBuffer.bits);//bitmap对graphicBuffer进行关联
   } else {
       // be safe with an empty bitmap.
       bitmap.setPixels(NULL);
   }
   //构造一个native的Canvas对象(SKCanvas),再返回这个Canvas对象,java层的Canvas对象其实只是对SKCanvas对象的一个简单包装,所有绘制方法都是转交给SKCanvas来做。
   Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    //bitmap对下关联了获取的内存buffer,对上关联了Canvas,把这个bitmap放入Canvas中
   nativeCanvas->setBitmap(bitmap);
   ...
   sp<Surface> lockedSurface(surface);
   lockedSurface->incStrong(&sRefBaseOwner);
   return (jlong) lockedSurface.get();
}

canvas.drawXXX

Skia深入分析

==SkCanvas是按照SkBitmap的方法去关联GraphicBuffer==

一、渲染层级 从渲染流程上分,Skia可分为如下三个层级:

  1. 指令层:SkPicture、SkDeferredCanvas->SkCanvas

这一层决定需要执行哪些绘图操作,绘图操作的预变换矩阵,当前裁剪区域,绘图操作产生在哪些layer上,Layer的生成与合并。

  1. 解析层:SkBitmapDevice->SkDraw->SkScan、SkDraw1Glyph::Proc

这一层决定绘制方式,完成坐标变换,解析出需要绘制的形体(点/线/规整矩形)并做好抗锯齿处理,进行相关资源解析并设置好Shader。

  1. 渲染层:SkBlitter->SkBlitRow::Proc、SkShader::shadeSpan等

这一层进行采样(如果需要),产生实际的绘制效果,完成颜色格式适配,进行透明度混合和抖动处理(如果需要)。

image

image

//Canvas.java
public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint) {
        super.drawLine(startX, startY, stopX, stopY, paint);
    }
//BaseCanvas.java  
public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint) {
        throwIfHasHwBitmapInSwMode(paint);
        nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
    }
//frameworks/base/core/jni/android_graphics_Canvas.cpp
static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
                     jfloat stopX, jfloat stopY, jlong paintHandle) {
    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
}
//external/skia/src/core/SkCanvas.cpp
void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) {
    SkPoint pts[2];
    pts[0].set(x0, y0);
    pts[1].set(x1, y1);
    this->drawPoints(kLines_PointMode, 2, pts, paint);
}

void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
    TRACE_EVENT0("skia", TRACE_FUNC);
    this->onDrawPoints(mode, count, pts, paint);
}

mSurface.unlockCanvasAndPost(canvas):

image

//Surface.cpp
status_t Surface::unlockAndPost()
{
    if (mLockedBuffer == 0) {
        ALOGE("Surface::unlockAndPost failed, no locked buffer");
        return INVALID_OPERATION;
    }
    int fd = -1;
    status_t err = mLockedBuffer->unlockAsync(&fd);//通过Gralloc模块,最后是操作的ioctl
    err = queueBuffer(mLockedBuffer.get(), fd);
    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = 0;
    return err;
}
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
    ...
    int i = getSlotFromBufferLocked(buffer); 
    ...
    IGraphicBufferProducer::QueueBufferOutput output;
    IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
            static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
            mTransform ^ mStickyTransform, fence, mStickyTransform,
            mEnableFrameTimestamps);
    ...
    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); 
    ...
    mQueueBufferCondition.broadcast();
    return err;
}
int Surface::getSlotFromBufferLocked(android_native_buffer_t* buffer) const {
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
        if (mSlots[i].buffer != NULL && mSlots[i].buffer->handle == buffer->handle) {
            return i;
        }
    }
    return BAD_VALUE;
}

我们看到了queueBuffer函数, 而在Surface的queueBuffer函数中调用了如下函数:

mGraphicBufferProducer->queueBuffer
status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { 

    //从input中获取一些列参数
    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
        &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
        &getFrameTimestamps);


    sp<IConsumerListener> frameAvailableListener;
    sp<IConsumerListener> frameReplacedListener;
    BufferItem item; //可以理解为一个待渲染的帧

    frameAvailableListener = mCore->mConsumerListener;
  
    ...下面就是对item的一系列赋值操作

    item.mAcquireCalled = mSlots[slot].mAcquireCalled; 
    item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; //根据slot获取GraphicBuffer。
    item.mCrop = crop;
    item.mTransform = transform &
            ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
    item.mTransformToDisplayInverse =
            (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
    item.mScalingMode = static_cast<uint32_t>(scalingMode);
    item.mTimestamp = requestedPresentTimestamp;
    item.mIsAutoTimestamp = isAutoTimestamp;
    
    ...

    if (frameAvailableListener != NULL) {
        frameAvailableListener->onFrameAvailable(item); //item是一个frame,准备完毕,要通知外界
    } else if (frameReplacedListener != NULL) {
        frameReplacedListener->onFrameReplaced(item);
    }

    addAndGetFrameTimestamps(&newFrameEventsEntry,etFrameTimestamps ? &output->frameTimestamps : nullptr);

    return NO_ERROR;
}

这个函数最终会将BufferItem的buffer清除,通知消费者的onFrameAvailable接口。然后消费者可以根据mSlots的序号再来拿buffer。

// ---------------------------------------------------------------------------
// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
// ---------------------------------------------------------------------------
void BufferLayer::onFrameAvailable(const BufferItem& item) {
    ...
    mFlinger->signalLayerUpdate();
}
void SurfaceFlinger::signalLayerUpdate() {
    mEventQueue->invalidate();
}