Android Skia和2D图形系统 .
Android Skia 和 2D 图形系统
1 Skia 概述
Skia 是 Google 一个底层的图形、图像、动画、 SVG 、文本等多方面的图形库,是 Android 中图形系统的引擎。
Skia 作为第三方软件放在 external 目录下: external/skia/ 。
Skia 的结构如下图所示:
主要包含三个库:
libcorecg.so: 包含 /skia/src/core 的部分内容,比如其中的 Region , Rect 是在 SurfaceFlinger 里面用来计算可视区域的;
libsgl.so: 包含 /skia/src/core|effects|images|ports|utils 的部分和全部内容,这个实现了 skia 大部分的图形效果,以及图形格式的编解码;
libskiagl.so: 包含 /skia/src/gl 里面的内容,主要用来调用 opengl 实现部分效果。
2 Skia 对上层的接口( API )
skia 的源文件及部分头文件都在 external/skia/src 目录下,导出的头文件在 external/skia/include 目录下。最主要的是 SKCanvas 类,几乎整个 Android GUI 系统的底层绘制都是由这个类来完成的。其头文件和源代码文件的路径分别为: external/skia/include/core/SKCanvas.cpp
external/skia/include/core/SKCanvas.h
SKCanvas 类主要有三种绘制功能:
a 基本图形绘制 ( 如 drawARGB,drawLine 函数 )
b 图像文件绘制( drawBitmap 函数)
c 文本绘制( drawText 函数)
3 Skia 的图像编解码部分
这部分的接口主要是:
external/include/image/SKImageDecoder.h // 把图像文件或者流解码到 skia 的内部内存 SKBitmap 中 ;
external/include/image/SKImageEncoder.h // 把 skia 内部内存 SKBitmap 编码成文件或流的形式;
这些接口需要具体的类实现,主要代码在 src/image 文件中。
4 Android 图形系统的 JNI 接口
主要提供了从 Skia 底层库到 Java 上层的支持,代码路径为:
frameworks/base/core/jni/android/graphic/
主要为 Canvas.cpp 文件。
5 Android 的图形包( graphics )
Android 图形类的包是 android.graphics, 它通过调用图形系统的 JNI 提供了对 Java 框架中图形系统的支持,在 Android 的 Java 框架中和 Java 应用程序中, 2D 绘制的功能(基本图形、图片文件,文字)也是通过调用它来实现的。代码路径为:
frameworks/base/graphics/java/android/graphics/
其中 Canvas.cpp 文件实现了 Android 图形系统中最重要的一个类 android.graphic.canvas 。
6 Android 2D 图形硬件加速
目前 Android 2D 图形硬件加速主要是通过 copybit 模块来实现, Copybit 是封装在 Android 系统 opengl 软件实现库( libagl )的一部分,仅对 openGL ES 2D API 进行封装,实现 openGL ES 2D API 到硬件的加速功能。
copybit 模块以 HAL 的形式实现,代码 hardware/msm7k/libcopybit/copybit.c
另外,在 http://code.google.com/p/skia/wiki/FAQ 看到关于 Skia 硬件加速和字体支持的相关信息:
Does Skia support HW acceleration?
There are two ways Skia can take advantage of HW.
1. Subclass SkCanvas
Since all drawing calls go through SkCanvas , those calls can be redirected to a different graphics API. SkGLCanvas has been written to direct its drawing calls to OpenGL. See src/gl/
2. Custom bottleneck routines
There are sets of bottleneck routines inside the blits of Skia that can be replace on a platform in order to take advantage of specific CPU features. One such example is the NEON SIMD instructions on ARM v7 devices. See src/opts/
Does Skia support Font hinting?
Skia has a built-in font cache, but it does not know how to actual render font files like TrueType into its cache. For that it relies on the platform to supply an instance of SkScalerContext. This is Skia's abstract interface for communicating with a font scaler engine. In src/ports you can see support files for FreeType, Mac OS X, and Windows GDI font engines. Other font engines can easily be supported in a like manner
Skia API的简单应用
1 Skia 绘图概述
使用 Skia 的 API 进行图形绘制时主要会用到一下几个类:
SkBitmap 、 SkCanvas 、 SkPaint 和 SkRect ,其中 SkBitmap 用来设置像素, SkCanvas 写入位图, SkPaint 设置颜色和样式, SkRect 用来绘制矩形。其实现代码主要在 src/core 目录下。
2 使用 Skia 绘图的步骤
a) 定义一个位图 32 位像素并初始化
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 200, 200);
其中 setConfig 为设置位图的格式,原型为 void setConfig(Config, int width, int height, int rowBytes = 0)
Config 为一个数据结构
enum Config {
kNo_Config, // 不确定的位图格式
kA1_Config, //1 位 ( 黑 , 白 ) 位图
kA8_Config, //8 位 ( 黑 , 白 ) 位图
kIndex8_Config, // 类似 windows 下的颜色索引表,具体请查看 SkColorTable 类结构
kRGB_565_Config, //16 位象素 565 格式位图,详情请查看 SkColorPriv.h 文件
kARGB_4444_Config, //16 位象素 4444 格式位图,详情请查看 SkColorPriv.h 文件
kARGB_8888_Config, //32 位象素 8888 格式位图,详情请查看 SkColorPriv.h 文件
kRLE_Index8_Config,
kConfigCount
};
b) 分配位图所占的空间
bitmap.allocPixels()
其实 allocPixels 为重载函数,原型为 bool allocPixels(SkColorTable* ctable = NULL)
参数 ctable 为颜色索引表,一般情况下为 NULL 。
c) 指定输出设备
SkCanvas canvas(new SkDevice(bitmap));
其中 canvas 为一个多构造函数,原型为
explicit SkCanvas(const SkBitmap& bitmap) ,
explicit SkCanvas(SkDevice* device = NULL)
explicit 关健字的意思为:不允许类型转换
输出设备可以为一个上下文 Device, 也可以指定为一张位图。
d) 设备绘制的风格
Paint paint;
SkRect r;
paint.setARGB(255, 255, 0, 0);
r.set(25, 25, 145, 145);
canvas.drawRect(r, paint);
paint 可以指定绘图的颜色,文本的大小及对齐方式,编码格式等等,因为以前位图的格式设置为 kARGB_8888_Config ,所以这里要设置绘制的颜色 setARGB(255, 255, 0, 0) ,第一位参数为透明颜色通道,其它三位分别为 R 、 G 、 B 。 r 设置要绘制的范围,最后通过 drawRect 绘制出指定区域的一个方形。
这样,一个红色的矩形就绘制成功了。
SkCanvas 主要完成三种绘制功能:
a 基本图形绘制 ( 如 drawARGB,drawLine 函数 )
b 图像文件绘制( drawBitmap 函数)
c 文本绘制( drawText 函数)
相关 API 有:
canvas.drawRect(rect, paint); canvas.drawOval(oval, paint); canvas.drawCircle(x, y, radius, paint); canvas.drawRoundRect(rect, rx, ry, paint); canvas.drawPath(path, paint); canvas.drawBitmap(bitmap, x, y, &paint); canvas.drawBitmapRect(bitmap, &srcRect, dstRect, &paint); canvas.drawBitmapMatrix(bitmap, matrix, &paint); canvas.drawText(text, length, x, y, paint); canvas.drawPosText(text, length, pos[], paint); canvas.drawTextOnPath(text, length, path, paint);
e)
例程
i )画点、线、圆、文字
#include "SkBitmap.h" #include "SkDevice.h" #include "SkPaint.h" #include "SkRect.h" #include "SkImageEncoder.h" #include "SkTypeface.h" using namespace std; int main() { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config,320,240); bitmap.allocPixels(); SkCanvas canvas(new SkDevice(bitmap)); SkPaint paint; // draw points with red. paint.setARGB(255, 255, 0, 0); paint.setStrokeWidth(4); canvas.drawPoint(40,30, paint); canvas.drawPoint(80,60, paint); canvas.drawPoint(120,90, paint); //draw a line with green. paint.setARGB(255, 0, 255, 0); paint.setStrokeWidth(4); canvas.drawLine(160,10,320,110,paint); //draw a circle with bule. paint.setARGB(255, 0, 0, 255); canvas.drawCircle(80,180,50,paint); //draw text with red SkTypeface *font = SkTypeface::CreateFromFile("simkai.ttf"); if ( font ) { paint.setARGB(255, 255, 0, 0); paint.setTypeface( font ); paint.setTextSize(24); canvas.drawText("HELLO!:)", 8, 200, 180, paint); } SkImageEncoder::EncodeFile("snapshot.png", bitmap,SkImageEncoder::kPNG_Type,100); return 0; }
程序执行后,得到如下输出结果:
ii) 图像的编解码
该例程目前测试只支持 .png 格式的图片, .jpg 还不支持,还未找到原因。
#include "SkBitmap.h" #include "SkDevice.h" #include "SkPaint.h" #include "SkRect.h" #include "SkImageEncoder.h" #include "SkImageDecoder.h" #include <iostream> using namespace std; int main() { int ret = -1; SkBitmap bitmap; //SkImageDecoder ret = SkImageDecoder::DecodeFile("./old.png", &bitmap); cout<< "get the decode type = "<< bitmap.config() << endl; //SkImageEncoder ret = SkImageEncoder::EncodeFile("new1.png",bitmap,SkImageEncoder::kPNG_Type,100); cout<< "encode data to png result = "<< ret<< endl; return 0; }
SkImageDecoder::DecodeFile("./old.png", &bitmap);
将 png 转换成位图格式,并将数据放到 bitmap 变量中
SkImageEncoder::EncodeFile("snapshot.png", bitmap,SkImageEncoder::kPNG_Type,/* Quality ranges from 0..100 */ 100);
将 bitmap 中的数据编码输出为 .png 格式,第一位参数为 png 文件路径,第二位为指定的输出位图,第三位为文件的类型,第四位参数指定了输出位图的质量,范围为 0..100 ,默认为 80 。
3 图形图像特效
src/effects 目录的文件主要实现一些图形图像的特效,包括 遮罩、浮雕、模糊、滤镜、渐变色、离散、透明以及 PATH 的各种特效等。
4 动画
src/animator 目录的文件主要实现了 Skia 的动画效果,Android不支持。
5 界面 UI 库
src/view 目录 构建了一套界面 UI 库。
组件包括 Window,Menu, TextBox, ListView, ProgressBar, Widget, ScrollBar,TagList,Image 等。
6 其它
a) src/gl 目录: 这部分是 skia 调用 OpenGL 或 OpenGL ES 来实现 3D 效果。
如果定义了 MAC ,则使用 OpenGL ,如果定义了 Android ,则使用嵌入式 系统 上的 esgl 三维图形库。
b)src/images 目录: 主要是 SkImageDecoder 和 SkImageEncoder 以及 SkMovie 。主要是用来处理 images 的,能处理的图像类型包括: BMP 、
JPEG/PVJPEG 、 PNG 、 ICO ,而 SkMovie 是用来处理 gif 动画的。
c) src/opts 目录:性能优化的代码。
d) src/pdf 目录: 处理 PDF 文档,用了一个 fpdfemb 库。
e) src/ports 目录: 这部分是 skia 的一些接口在不同系统上的实现,平台相关的代码,比如字体、线程、时间等, 主要包括几个部分: Font , Event , File , Thread , Time , XMLParser
这些与 Skia 的接口,需要针对不同的 操作系统 实现。
f) src/svg 目录: 矢量图像,Android不支持。
SkSVGPath, SkSVGPolyline, SkSVGRect, SkSVGText, SkSVGLine, SkSVGImage, SkSVGEllipse 等等。
g) src/text 目录:???
h) src/utils 目录: 是一些辅助工具类。
SkCamera, SkColorMatrix,SkOSFile,SkProxyCanvas,SkInterpolator 等文件。
i) src/xml : 这是处理 xml 数据的部分, skia 在这里只是对 xml 解析器做了一层包装,具体的 xml 解析器的实现需要根据不同的操作系统及宿主程序来实现。
j) Third-party library
除了自身的所有文件外, skia 还使用了一些 third-party library 以及包含了不少 linux 上的头文件。
通过分析 skia 源程序,发现 skia 主要使用以下几个第三方库:
Zlib ,处理数据的压缩和解压缩
Jpeglib ,处理 jpeg 图像的编码解码
Pnglib ,处理 png 图像的编码解码
giflib ,处理 gif 图像
fpdfemb ,处理 pdf 文档
skia 还需要一些 linux/unix 下的头文件(可能还需要更多):
stdint.h
unistd.h
features.h
cdefs.h
stubs.h
posix_opt.h
types.h
wordsize.h
typesizes.h
confname.h
getopt.h
mman.h