Skia深入分析5——skia文字绘制的实现

文字绘制主要包括编码转换(主要是中文)、字形解析(点线或image)和实际渲染三个步骤。在这个过程中,字形解析和实际渲染均是耗时步骤。Skia对文字解析的结果做了一套缓存机制。在中文字较多,使用多种字体,绘制的样式(粗/斜体)有变化时,这个缓存会变得很大,因此Skia文字缓存做了内存上的限制。

1、SkPaint

文字绘制与SkPaint的属性相关很大,先回头看下SkPaint相关的属性

class SkPaint
{
private
    SkTypeface*     fTypeface;//字体
    SkPathEffect*   fPathEffect;//路径绘制效果
    SkShader*       fShader;//取色器
    SkXfermode*     fXfermode;//混合模式,类似OpenGL里面的Blend设置
    SkColorFilter*  fColorFilter;//图像绘制时,自定义图像采样函数时使用
    SkMaskFilter*   fMaskFilter;//路径绘制时,按有无像素做进一步自定义改进处理时使用
    SkRasterizer*   fRasterizer;//路径绘制时自定义生成像素点的算法时使用
    SkDrawLooper*   fLooper;//循环绘制,SkCanvas里面的第二重循环,一般不用关注
    SkImageFilter*  fImageFilter;//SkCanvas的第一重循环,绘制后做后处理用,一般不用关注
    SkAnnotation*   fAnnotation;//暂时没用到的属性

    SkScalar        fTextSize;//文字大小

    SkScalar        fTextScaleX;//文字水平方向上的拉伸,仅用于PDF绘制
    SkScalar        fTextSkewX;//文字横向扭曲度,仅用于PDF绘制

    SkColor         fColor;//纯色,在fShader为空时使用
    SkScalar        fWidth;//带边界时(kStroke_Style/kStrokeAndFill_Style)生效,边界的宽度 
    SkScalar        fMiterLimit;//drawPath时,连接各个path片断时,要求的圆滑连接阈值,Join 类型为默认的kMiter_Join时无效
    /*一组不超过32位的属性*/
    union {
        struct {
            // all of these bitfields should add up to 32
            unsigned        fFlags : 16;//包含所有的0/1二值属性:
            /*
               kAntiAlias_Flag       = 0x01,//是否抗锯齿
               kDither_Flag          = 0x04,//是否做抖动处理
               kUnderlineText_Flag   = 0x08,//是否绘制文字下划线
               kStrikeThruText_Flag  = 0x10,//目前未看到其作用
               kFakeBoldText_Flag    = 0x20,
               kLinearText_Flag      = 0x40,
               kSubpixelText_Flag    = 0x80,//文字像素精确采样
               kDevKernText_Flag     = 0x100
               kLCDRenderText_Flag   = 0x200
               kEmbeddedBitmapText_Flag = 0x400,
               kAutoHinting_Flag     = 0x800,
               kVerticalText_Flag    = 0x1000,//是否竖向绘制文字
               kGenA8FromLCD_Flag    = 0x2000,
               kDistanceFieldTextTEMP_Flag = 0x4000,
               kAllFlags = 0xFFFF
             */

            unsigned        fTextAlign : 2;//文字对齐方式,取值如下:
            /*
            enum Align {
                kLeft_Align,//左对齐
                kCenter_Align,//居中
                kRight_Align,//右对齐
            };
            */

            unsigned        fCapType : 2;//边界连接类型,分无连接,圆角连接,半方形连接
            unsigned        fJoinType : 2;//Path片断连接类型
            
            unsigned        fStyle : 2;//绘制模式,填充边界/区域
            /*
               enum Style {
               kFill_Style, //填充区域
               kStroke_Style,//绘制边界
               kStrokeAndFill_Style,//填充区域并绘制边界
               };
             */

            unsigned        fTextEncoding : 2;//文字编码格式,支持如下几种
            enum TextEncoding {
                kUTF8_TextEncoding,//utf-8,默认格式
                kUTF16_TextEncoding,
                kUTF32_TextEncoding,
                kGlyphID_TextEncoding
            };

            unsigned        fHinting : 2;
            unsigned        fFilterLevel : 2;//在图像绘制时提到的采样质量要求
            //unsigned      fFreeBits : 2;
        };
        uint32_t fBitfields;
    };
    uint32_t fDirtyBits;//记录哪些属性被改变了,以便更新相关的缓存
};

2、字体绘制基本流程


SkCanvas
绘制文字和下划线

SkDraw

两种绘制方式:

(1)将文字解析为路径,然后绘制路径,缓存路径(drawText_asPaths)。

void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
                              SkScalar x, SkScalar y,
                              const SkPaint& paint) const {
    SkDEBUGCODE(this->validate();)

    SkTextToPathIter iter(text, byteLength, paint, true);

    SkMatrix    matrix;
    matrix.setScale(iter.getPathScale(), iter.getPathScale());
    matrix.postTranslate(x, y);

    const SkPath* iterPath;
    SkScalar xpos, prevXPos = 0;

    while (iter.next(&iterPath, &xpos)) {
        matrix.postTranslate(xpos - prevXPos, 0);
        if (iterPath) {
            const SkPaint& pnt = iter.getPaint();
            if (fDevice) {
                fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
            } else {
                this->drawPath(*iterPath, pnt, &matrix, false);
            }
        }
        prevXPos = xpos;
    }
}

(2)将文字解析为Mask(32*32的A8图片),然后绘制模板,缓存模板。

    SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();

    SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, fMatrix);
    SkGlyphCache*       cache = autoCache.getCache();

    // transform our starting point
    {
        SkPoint loc;
        fMatrix->mapXY(x, y, &loc);
 
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值