2311skia,02绘制图片

Skia绘图的流程

1,API用法

(1)drawBitmap

void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint = NULL);

位图画到x,y的位置(自身是个平移,需要叠加SkCanvas中的矩阵状态).

(2)drawBitmapRectdrawBitmapRectToRect

void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint = NULL);
void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags);

src源图矩阵,画到目标dst区域.

最后flags安卓上为了gpu绘画效果而加上去的,在CPU绘画中不需要关注.

(3)drawSprite

void drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint);

无视SkCanvas的矩阵状态,把位图平移到x,y的位置.

(4)drawBitmapMatrix

void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);

绘画带矩形变换的位图,需要叠加SkCanvas矩形变换.

(5)drawRect

void drawRect(const SkRect& r, const SkPaint& paint);

最通用方法,要加额外效果时常用,如要绘画重复纹理.Tile两个参数是,OpenGL纹理贴图中水平垂直方向上的边界处理模式.
比如画圆角矩形图标时,把方图片裁剪成圆等.

下面是个示例程序:

#include "SkBitmapProcShader.h"
#include "SkCanvas.h"
#include "SkBitmap.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkRect.h"

int main()
{
    const int w = 1080;
    const int h = 1920;
    /*准备目标图片和源图片*/
    SkBitmap dst;
    dst.allocPixels(SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType));
    SkCanvas c(dst);
    SkBitmap src;
    SkImageDecoder::DecodeFile("test.jpg", &src);
    /*各种绘图方法使用示例*/
    {
        c.drawBitmap(src, 0, 0, NULL);
    }
    {
        c.drawSprite(src, 400, 400, NULL);
    }
    {
        SkRect dstR;
        r.set(29, 29, 100, 100);
        SkRect srcR;
        r.set(0,0,40,50);
        c.drawBitmapRectToRect(src, &srcR, dstR, NULL);
    }
    {
        SkMatrix m;
        m.setScale(1.4,4.3);
        c.drawBitmapMatrix(src, m, NULL);
    }
    {
        SkRect dstRect;
        dstRect.set(100,100,480,920);
        SkPaint paint;
        SkMatrix m;
        m.setScale(3.2, 4.1);
        SkShader* shader = CreateBitmapShader(src, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, m, NULL);
        paint.setShader(shader);
        SkSafeUnref(shader);
        c.drawRect(dstRect, paint);
    }
    /*输出图片*/
    SkImageEncoder::EncodeFile("output.jpg", dst, SkImageEncoder::kJPEG_Type, 100);
    return 1;
}

2,解析流程

(1)SkCanvas两重循环调用SkBitmapDevice,进而调用SkDraw.
SkDraw中,drawBitmap渲染函数统一为:

void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, const SkPaint& origPaint) const;

(2)Sprite简易模式

满足如下条件时,为Sprite简易模式.
代码见external/skia/src/core/SkDraw.cppdrawBitmap函数.
a:

(bitmap.colorType() != kAlpha_8_SkColorType && just_translate(matrix, bitmap))

kAlpha_8_SkColorType的图像只有一个α通道,按drawMask方式处理,按图像的α预乘绘图中的颜色,再叠加目标区域上.

just_translate表示矩形为不涉及旋转缩放的仅平移矩阵,位图像素点SkCanvas绑定的dstBitmap像素点一一对应.
b:

clipHandlesSprite(*fRC, ix, iy, bitmap))

指当前SkCanvas裁剪区域不需要考虑抗锯齿,或完全包含了位图渲染区域.必须在裁剪区域内渲染SkCanvas,因此如果图像跨越了裁剪区域边界,且裁剪区域要考虑抗锯齿,则在边界上要特殊处理.

注:设置裁剪区域API:

void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA)

doAA即是否在r的非整数边界时考虑抗锯齿.
满足条件,由SkScan::FillIRect按每个裁剪区域调用SkSpriteBlitterblitRect来创建SkSpriteBlitter.
此时可直接转换颜色及合成透明度,来渲染,不必抗锯齿和图像插值,也就不必取样--混合,性能最高.

满足条件后用ChooseSprite去选一个SkSpriteBlitter
详细代码见external/skia/src/core/SkBlitter_Sprite.cpp中的ChooseSprite函数.

一般不用此函数,然后就开始转回drawRect流程.

(3)创建BitmapShader

在:

SkAutoBitmapShaderInstall install(bitmap, paint);

句中,为绘图创建了bitmapShader:

fPaint.setShader(CreateBitmapShader(src, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, localMatrix, &fAllocator));

然后,就可用drawRect画图像了.

(4)drawRect

不能用SkSpriteBlitterdrawRect.

这里有非常多的分支,只讲绘画实矩形.

SkAutoBlitterChoose->SkBlitter::Choose中,根据Canvas绑定的位图像素模式,绘图属性选择blitter.
绘图时绘图Shader(SkBitmapProcShader),因此是选的是带ShaderBlitter,比如适应ARGB格式的SkARGB32_Shader_Blitter.

(5)SkScan

SkScan中,对每个裁剪区域,将其与绘画的rect求交,然后渲染相交区域.此外,必要时抗锯齿.

抗锯齿就是对浮点的坐标,按其偏离整数的程度给一个α权重,颜色再乘以此权重(减淡颜色),再画上去.

在绘画矩形时,SkScan先用blitV绘画左右边界,再用blitAntiH绘画上下边界,中间大块不需要考虑抗锯齿,因而用blitRect.

(6)blitRect

这一步先通过ShadershadeSpan方法取对应位置的像素,再通过SkBlitRow过程叠加此像素.

如果不考虑混合模式,可跳过过程.

参考代码:external/skia/src/core/SkBlitter_ARGB32.cpp中的blitRect

(7)shadeSpan

这里只考虑SkBitmapProcShadershadeSpan,这主要是图像采样.详细代码见external/skia/src/core/SkBitmapProcShader.cpp

对每一个目标点,先通过matrixProc取出需要参考的源图像素,然后用sampleProc合成这些像素为一个像素值.(和OpenGL里面的texture2D函数原理类似).

若存在shaderProc(线性插值时,可优化上面的步骤,完全可取出一群像素来计算插值),以shaderProc代替上面的两步流程,来优化性能.

3,解析SkBlitter接口

(1)blitH

virtual void blitH(int x, int y, int width);

x,y坐标开始,渲染一行像素

(2)blitV

virtual void blitV(int x, int y, int height, SkAlpha alpha);

x,y开始,渲染一列像素,按α减淡颜色

(3)blitAntiH

virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);

该函数的用来渲染上下边界,并搞抗锯齿.

(4)blitRect

virtual void blitRect(int x, int y, int width, int height);

绘画矩形区域,这里不搞几何变换,抗锯齿.

(5)blitMask

virtual void blitMask(const SkMask& mask, const SkIRect& clip);

绘画文字时使用,颜色乘以掩码中的透明度,再叠加.

你可能感兴趣的:(skia,c++,cpp,skia)