Skia引擎API整理介绍

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 <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

 

 

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);

    }

}

posted @ 2012-05-03 11:30  Vegetable Bird  阅读(9488)  评论(0编辑  收藏  举报