http://code.google.com/p/skia/
svn checkout http://skia.googlecode.com/svn/trunk/ skia-read-only
Skia引擎重要类简介
(PS: 注意是简介了,观众不要要求太高,我也是在摸索中整理的文档)
1. SkCanvas
这个类是Skia引擎的一个核心类,他封装了所有对设备进行的画图操作。这个类自身包含了一个设备的引用,以及一个矩阵和裁剪栈。所有的画图操作, 都是在经过栈内存放的矩阵变幻之后才进行的(这点和OpenGL类似)。当然,最终显示给用户的图像,还必须经过裁剪堆栈的运算。
SkCanvas记录着整个设备的绘画状态,而设备上面绘制的对象的状态又是由SkPaint类来记录的,SkPaint类作为参数,传递给不同 SkCanvas类的成员函数drawXXXX().(比如:drawPoints, drawLine, drawRect, drawCircle)。SkPaint类里记录着如颜色(color), 字体(typeface), 文字大小(textSize), 文字粗细(strokeWidth), 渐变(gradients, patterns)等。
SkCanvas类的主要成员函数:
> 构造函数,给定一个Bitmap或者Device,在给定的这个对象上进行画图,Device可以为空。
SkCanvas(const SkBitmap& bitmap);
SkCanvas(SkDevice* device = NULL);
> setViewport, getViewport, 这2个函数只有在支持OpenGL视图时才有效。
> save, saveLayer, saveLayerAlpha, restore, 这4个函数用于保存和恢复显示矩阵,剪切,过滤堆栈,不同函数有不同的附加功能。
> 移位,缩放,旋转,变形函数。
translate(SkiaScalar dx, SkiaScalar dy);
scale(SkScalar sx, SkScalar sy);
rotate(SkScalar degrees);
skew(SkScalar sx, SkScalar sy);
> 指定具体矩阵,进行相应的变换的函数,以上4个方法都可以通过定义特定的矩阵,再调用此函数实现。
cancat(const SkMatrix& matrix);
> 图像剪辑,把指定的区域显示出来。
clipRect(SkRect&...);
clipPath(SkPath&...);
clipRegion(SkRegion&...);
> 在当前画布内画图,有以下多种画图方式:
drawARGB(u8 a, u8 r, u8 g, u8 b....) 给定透明度以及红,绿,兰3色,填充整个可绘制区域。
drawColor(SkColor color...) 给定颜色color, 填充整个绘制区域。
drawPaint(SkPaint& paint) 用指定的画笔填充整个区域。
drawPoint(...)/drawPoints(...) 根据各种不同参数绘制不同的点。
drawLine(x0, y0, x1, y1, paint) 画线,起点(x0, y0), 终点(x1, y1), 使用paint作为画笔。
drawRect(rect, paint) 画矩形,矩形大小由rect指定,画笔由paint指定。
drawRectCoords(left, top, right, bottom, paint), 给定4个边界画矩阵。
drawOval(SkRect& oval, SkPaint& paint) 画椭圆,椭圆大小由oval矩形指定。
drawCicle(cx, cy, radius, paint), 给定圆心坐标和半径画圆。
drawArcSkRect& oval...) 画弧线,用法类似于画椭圆。
drawRoundRect(rect, rx, ry, paint) 画圆角矩形,x, y方向的弧度用rx, ry指定。
drawPath(path, paint) 路径绘制,根据path指定的路径绘制路径。
drawBitmap(SkBitmap& bitmap, left, top, paint = NULL) 绘制指定的位图, paint可以为空。
drawBitmapRect(bitmap, src, dest, paint=NULL), 绘制给定位图的一部分区域,此区域由src指定,然后把截取的部分位图绘制到dest指定的区域,可能进行缩放。
drawBitmapMatrix(bitmap, matrix, paint=NULL), 功效同上,可以通过给定矩阵来进行裁剪和缩放变换。
drawSprite(bitmap, left, top, paint=NULL), 绘制位图,不受当前变换矩阵影响。
drawText(void* text, byteLength, x, y, paint), 以(x,y)为起始点写文字,文字存储在text指针内,长度有byteLength指定。
drawPosText(...) 功能同上,不过每个文字可以单独指定位置。
drawPosTextH(...) 功能同上,不过由一个变量指定了当前所有文字的统一Y坐标,即在同一条水平线上以不同的间隔写字。
drawTextOnPathHV, drawTextOnPath, drawTextOnPath, 以不同方式在给点定的path上面绘制文字。
drawPicture(SkPicture& picture) 在画布上绘制图片,比较高效的绘图函数。
drawShape(SkShape*) 在画布上绘制指定形状的图像。
drawVertices(...) 绘制点,可以有纹理,颜色,等附加选项。
1 Skia 绘图概述
Skia 是 Google 一个底层的图形、文本、图像、动画等多方面的图形库,是 Android 中图形系统的引擎。 Skia 作为第三方软件放在 external 目录下: external/skia/ 。 skia 的源文件及部分头文件都在 src 目录下,导出的头文件在 include 目录下。
使用 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
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
3D图片旋转,旋转窗口我看行
SkBitmap *pskBitmap;
SkCanvas *pskCanvas;
BITMAPINFO *lpbmi;
HWND g_hWnd;
SkBitmap *bkBitmap;//背景图片
SkRect g_rtImg;// 图片最初按钮。
SkRect g_rtClip;//矩阵裁剪用 ,做图片旋转时,每次旋转时的裁剪会用到上一次的裁剪范围。
//g_rtClip是共用裁剪范围,多个不同的位置共用,每次旋转前初始化为要旋转图片的原始位置
//初始化背景图片,
void MyInitBkImage(char *filename)
{
SkFILEStream stream(filename);
SkImageDecoder * coder = SkImageDecoder::Factory(&stream);
if (coder)
{
bkBitmap = new SkBitmap();
coder->decode(&stream,bkBitmap,SkBitmap::kRGB_565_Config,SkImageDecoder::kDecodePixels_Mode);
}
}
//整体初始化
void MyInit()
{
pskBitmap = new SkBitmap();
pskBitmap->setConfig(SkBitmap::kRGB_565_Config,800,480);
pskBitmap->allocPixels();//分配位图所占空间
pskCanvas = new SkCanvas();
pskCanvas->setBitmapDevice(*pskBitmap);
lpbmi = (BITMAPINFO*)malloc( sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(2) );
//printf("%d,%d\n",sizeof(BITMAPINFOHEADER),sizeof(BITMAPINFO));40,44
memset( lpbmi, 0, sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(2) );//必须同上方一直
lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);//位图信息头大小 40字节
lpbmi->bmiHeader.biWidth = 800;
lpbmi->bmiHeader.biHeight = -480;
lpbmi->bmiHeader.biPlanes = 1;
lpbmi->bmiHeader.biBitCount = 16; //16位位图 565模式0xF800、0x07E0、0x001F
lpbmi->bmiHeader.biCompression = BI_BITFIELDS; //压缩参数 BI_RGB=0表示无压缩,
lpbmi->bmiHeader.biSizeImage = 0;
lpbmi->bmiColors[0].rgbBlue = 0;
lpbmi->bmiColors[0].rgbGreen = 0xF8; //248?
lpbmi->bmiColors[0].rgbRed = 0;
lpbmi->bmiColors[0].rgbReserved = 0;
lpbmi->bmiColors[1].rgbBlue = 0xE0; //224
lpbmi->bmiColors[1].rgbGreen = 0x07; //7
lpbmi->bmiColors[1].rgbRed = 0;
lpbmi->bmiColors[1].rgbReserved = 0;
lpbmi->bmiColors[2].rgbBlue = 0x1F; //31
lpbmi->bmiColors[2].rgbGreen = 0;
lpbmi->bmiColors[2].rgbRed = 0;
lpbmi->bmiColors[2].rgbReserved = 0;
MyInitBkImage("\\USER\\skia\\bk.png");
g_rtImg.setLTRB(151,214,249,346); //初始化图片位置
//g_rtClip 在每次旋转前初始化
}
//画图片filename,rt为其范围,图片没有保存,每次临时加载
void DrawImage(char * filename,SkCanvas *canvas, SkRect rt, SkPaint *paint)
{
int ti = GetTickCount();
SkFILEStream stream(filename);
SkImageDecoder* coder = SkImageDecoder::Factory(&stream);
SkBitmap *bitmap;
if (coder)
{
//printf(" file %s code success\n",filename);
bitmap = new SkBitmap();
coder->decode(&stream, bitmap, SkBitmap::kRGB_565_Config,
SkImageDecoder::kDecodePixels_Mode);
}
else
{
printf(" file %s code fail\n",filename);
return;
}
//printf("24bit800*480png,code time =%d\n",GetTickCount()-ti);//367
ti = GetTickCount();
canvas->drawBitmap(*bitmap,rt.fLeft, rt.fTop);
//printf("24bit800*480png,draw time =%d\n",GetTickCount()-ti);//12
delete bitmap;
return;
}
//画背景
void DrawBKImage()
{
SkIRect rt;
rt.setXYWH(0,0,800,480);
int ti = GetTickCount();
pskCanvas->drawBitmap(*bkBitmap,rt.fLeft,rt.fTop);
printf("--------------time draw bk 24bit800*480=%d\n",GetTickCount()-ti);//11
}
//画图片filename,效果使其绕Y轴旋转rotateY角度,调用DrawImage()
void DrawRotateYImage(char * filename,int rotateY,SkRect rtImg)
{
SkRect rtClip = g_rtClip;//保留上次裁剪范围
pskCanvas->resetMatrix();
SkMatrix matrix;
Sk3DView sk3DView;
sk3DView.rotateY(rotateY); //绕Y轴旋转
sk3DView.getMatrix(&matrix);
matrix.preTranslate(-(rtImg.fLeft+rtImg.width()/2), 0);
matrix.postTranslate((rtImg.fLeft+rtImg.width()/2), 0);
matrix.mapRect(&g_rtClip,rtImg); //两个参数都是SkRect类型
//matrix.mapRect 作用:src经过matrix变化,形成dst
//图片的最初范围经过matrix变化(每次绕Y轴旋转角度不一样),得出新的裁剪范围
rtClip.join(g_rtClip); //计算最终裁剪范围
g_rtClip = rtClip ; //保存裁剪范围,供下次计算最终裁剪范围
//DrawBKImage();//此处画背景,不然会保留不同ratateY角度图片的痕迹 ,放在此处 画背景用时多 ,为4或者3,
pskCanvas->save();
pskCanvas->clipRect(rtClip);//矩阵裁剪
DrawBKImage();//此处画背景,不然会保留不同ratateY角度图片的痕迹 放在此处画背景用时小 ,为0或者1,
//具体画的内容,与pskCanvas画布的裁剪有关系?
//pskCanvas->save(SkCanvas::kMatrix_SaveFlag); 可以去掉,之前已经有pskCanvas->save();
pskCanvas->concat(matrix);
DrawImage(filename,pskCanvas,rtImg,NULL);
//此处的位置参数须是图片原始位置,不能是裁剪范围,否则显示的位置偏离
//pskCanvas->restore(); 与save对应
pskCanvas->restore();
//pskCanvas->resetMatrix();
}
//触发图片旋转函数
void MyLButtonDown()
{
g_rtClip = g_rtImg; //初始裁剪范围为要画图片的正常范围。
for (int i =0;i<=10;i++)
{
DrawRotateYImage("\\user\\skia\\music-n.png",36*(i+0),g_rtImg);
HDC dc = GetDC(g_hWnd);
SetDIBitsToDevice(dc, 0, 0, 800, 480, 0, 0, 0, 800, pskBitmap->getPixels(), lpbmi, DIB_RGB_COLORS);
//将数据显示到屏幕上
ReleaseDC(g_hWnd,dc);
}
}