关闭

Introduce My Work

3168人阅读 评论(0) 收藏 举报
分类:



我在LinkedIn上面的个人简介是:

Roger at UC Mobile Ltd. (www.uc.cn), focus on graphics stack (rendering architecture) research of WebKit based Browser in Android platform, include the graphics stack of WebKit itself along with the display subsystem of Android platform, and design how to combine the best of both them to achieve butter graphics performance of page rendering.

简单的说就是专注于Android平台基于WebKit的浏览器绘图堆栈的研究,可是,到底什么是所谓的”Android平台基于WebKit的浏览器绘图堆栈“,下面我用一个例子来说明。

我们来考究一个最简单的HTML5 Canvas在Android平台基于WebKit的浏览器上的实现:

  1. 如果网页包含Canvas标签,或者由JavaScript动态生成,都会导致WebKit内核生成一个HTMLCanvasElement Node对象;
  2. 当JS从Canvas元素获得一个2D Context对象时(var ctx = canvas.getContext(“2d”)),实际上这个Context JS对象会跟一个C++的CanvasRenderingContext2D对象绑定,JS在这个Context上的全部调用都会被虚拟机转发给对应的CanvasRenderingContext2D对象;
  3. WebKit内部的2D绘图是基于GraphicsContext的,所以CanvasRenderingContext2D对象需要一个GraphicsContext来执行真正的2D绘图操作,它会向HTMLCanvasElement对象发出请求,通过它的drawingContext()方法返回一个GraphicsContext;
  4. 而HTMLCanvasElement则把这个请求转发给内部的ImageBuffer对象,所以真正用于绘图的GraphicsContext是由ImageBuffer提供的;
  5. ImageBuffer顾名思义,是用来管理一块位图缓存,默认情况下,它会分配一块内存,把这块内存封装成一个位图(在Android平台是SkBitmap),然后通过这个位图创建一个平台相关的绘图上下文PlatformGraphicsContext(在Android平台是SkCanvas和PlatformGraphicsContextSkia),最后把这个PlatformGraphicsContext封装成一个平台无关的GraphicsContext返回;
  6. 从上面可知,当JS通过Canvas元素获得的Context对象绘图时,最终是绘制到HTMLCanvasElement对象内部的ImageBuffer对象管理的一块内存里面;
上面仅仅说明了JS调用本身的绘制路径,要最终在屏幕上看到结果,我们还需要把ImageBuffer里面的内容绘制到窗口上:

  1. 当JS执行Context对象的每一个绘图操作时,对应的HTMLCanvasElement对象都会对外广播一个内容发生变化的通知(canvasChanged);
  2. 这个通知会被转换成网页内容的某个区域的重绘请求;
  3. 这个网页内容的重绘请求会被WebKit内核的适配层拦截,在Android平台上,它最终会被转换成用于显示这个网页的View对象的某个区域的重绘请求(View.invalidate),重绘区域需要经过从网页的内容坐标系到View的显示坐标系的坐标转换;
  4. 当Android当前Activity的View Hierachy的某个View需要重绘时,Android会在稍后的某个一个时间点发起一个View Hierachy的遍历操作(ViewRootImpl.doTraversal);
  5. 在这个View Hierachy的遍历操作中,Android会生成一个Canvas对象,然后逐个调用View的onDraw方法,让View把自身的内容通过这个Canvas绘制到当前的窗口上,这个Canvas对象实际上从当前窗口对应的Surface对象中获得,而Surface对象需要从当前窗口的Graphics Buffer队列中取出一块空闲的Buffer(这个Buffer由系统的窗口混合器SurfaceFlinger分配),然后把这块Buffer封装成一个SkBitmap,再根据SkBitmap生成SkCanvas,再根据SkCanvas生成Java Canvas…
  6. 当用于显示网页的View对象的onDraw方法被Android系统调用到时,它需要重新把C++的SkCanvas对象从Java的Canvas对象中取出来,然后把这个SkCanvas包装成WebKit所需的GraphicsContext对象,传给WebKit让它去绘制网页,当WebKit绘制到对应这个Canvas元素的Render Object(RenderHTMLCanvas)时,RenderHTMLCanvas实际上会把这个绘制请求转发给它对应的HTMLCanvasElement对象(就是前面说的那一个),而HTMLCanvasElement则把自己的ImageBuffer对象通过传入的GraphicsContext最终绘制到当前窗口上;
  7. 当Android遍历完整个View Hierachy,所有View都通过传入的Canvas绘制到了当前的窗口上,但这时我们还未能在屏幕上看到更新的内容,因为实际上我们是绘制到了当前窗口的back buffer,Android还需要把绘制完的back buffer解锁,放回窗口的Graphics Buffer队列,通知SurfaceFlinger窗口的内容发生了变化(Surface.unlockAndPost);
  8. 当SurfaceFlinger收到窗口更新的通知时,它会切换窗口的前后台缓存(Page Flip),然后逐个把当前可见的窗口的front buffer拷贝到系统的framebuffer里面去,这样我们最终才在屏幕上看到Canvas绘制的结果(Android 4.0开始,如果硬件支持,可以省略掉将窗口front buffer拷贝到系统framebuffer这一步,由display hardware自己负责);
上面的例子足够复杂吗,实际上已经省略了很多不太重要的部分,也简化了一些比较复杂的概念比如Android的Native Window和Window Compositing机制,并且这仅仅是一个最最最简单的实现!!!

如果我们需要考虑支持:
  1. 支持像系统浏览器一样的多线程渲染架构或者像Chrome一样的多进程渲染架构;
  2. 支持Android的GPU加速绘图(从Android3.0 开始支持);
  3. 支持WebKit的图层混合加速(Accelerated Compositing);
  4. 支持硬件加速的2D Canvas实现(Accelerated 2D Canvas);
上面的每一个选项都会让本来的流程再加倍的复杂,而这些就是我的主要工作内容 ^_^
0
0

猜你在找
【直播】机器学习&深度学习系统实战(唐宇迪)
【直播】Kaggle 神器:XGBoost 从基础到实战(冒教授)
【直播回放】深度学习基础与TensorFlow实践(王琛)
【直播】计算机视觉原理及实战(屈教授)
【直播】机器学习之凸优化(马博士)
【直播】机器学习之矩阵(黄博士)
【直播】机器学习之概率与统计推断(冒教授)
【直播】机器学习之数学基础
【直播】TensorFlow实战进阶(智亮)
【直播】深度学习30天系统实训(唐宇迪)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:238865次
    • 积分:3144
    • 等级:
    • 排名:第10555名
    • 原创:62篇
    • 转载:0篇
    • 译文:2篇
    • 评论:95条
    文章分类
    最新评论