skia_skia和默认选哪个 - CSDN
精华内容
参与话题
  • Skia深入分析

    万次阅读 2015-05-08 18:42:18
    从渲染流程上分,Skia可分为如下三个层级: 1、指令层:SkPicture、SkDeferredCanvas->SkCanvas 这一层决定需要执行哪些绘图操作,绘图操作的预变换矩阵,当前裁剪区域,绘图操作产生在哪些layer上,Layer的生成与...
    一、渲染层级
    从渲染流程上分,Skia可分为如下三个层级:
    1、指令层:SkPicture、SkDeferredCanvas->SkCanvas
    这一层决定需要执行哪些绘图操作,绘图操作的预变换矩阵,当前裁剪区域,绘图操作产生在哪些layer上,Layer的生成与合并。
    2、解析层:SkBitmapDevice->SkDraw->SkScan、SkDraw1Glyph::Proc
    这一层决定绘制方式,完成坐标变换,解析出需要绘制的形体(点/线/规整矩形)并做好抗锯齿处理,进行相关资源解析并设置好Shader。
    3、渲染层:SkBlitter->SkBlitRow::Proc、SkShader::shadeSpan等

    这一层进行采样(如果需要),产生实际的绘制效果,完成颜色格式适配,进行透明度混合和抖动处理(如果需要)。


    二、主要类介绍
    1、SkCanvas
    这是复杂度超出想像的一个类。
    (1)API设计
    a、创建:
    在Android中,主要的创建方法是由SkBitmap创建SkCanvas:
    explicit SkCanvas(const SkBitmap& bitmap);
    这个方法是由bitmap创建一个SkBitmapDevice,再将这个SkBitmapDevice设定为SkCanvas的渲染目标。


    5.0之后提供了一个快捷方法创建SkCanvas:
    static SkCanvas* NewRasterDirect(const SkImageInfo&, void*, size_t);
    这样Android的GraphicBuffer就不需要建一个SkBitmap和它关联了,可以解除SkBitmap类和android runtime的关系(虽然如此,目前Android5.0上,还是按建SkBitmap的方法去关联GraphicBuffer)。


    5.0之后引入的离屏渲染:
    static SkCanvas* NewRaster(const SkImageInfo&);
    创建一个SkCanvas,绘制的内容需要通过readPixels去读取,仍然是CPU绘图的方式。(个人觉得这个是转入GPU硬件加速的一个比较方便的接口,不知道出于什么考虑还是用CPU绘图。)


    b、状态:
    矩阵状态:
    矩阵决定当前绘制的几何变换
    rotate、skew、scale、translate、concat
    裁剪状态:
    裁剪决定当前绘制的生效范围
    clipRect、clipRRect、clipPath、clipRegion
    保存与恢复:
    save、saveLayer、saveLayerAlpha、restore
    c、渲染:
    大部分渲染的API都可由这三个组合而成:
    drawRect(矩形/图像绘制)、drawPath(不规则图形图像绘制)和drawText(文本绘制)
    d、像素的读取与写入
    readPixels、writePixels
    这两个API主要由device实现,考虑到不同绘图设备的异质性。


    (2)MCRec状态栈
    fMCStack是存储的全部状态集,fMCRec则是当前的状态。
    在 save saveLayer saveLayerAlpha 时,会新建一个MCRec,在restore时,销毁栈顶的MCRec。
    (代码见:SkCanvas.cpp internalSave函数,通过这段代码可以了解一下new的各种用法~。)
    每个状态包括如下信息:
    class SkCanvas::MCRec {
    public:
        int             fFlags;//保存的状态标识(是否保存矩阵/裁剪/图层)
        SkMatrix*       fMatrix;//矩阵指针,若这个状态有独立矩阵,则指向内存(fMatrixStorage),否则用上一个MCRec的fMatrix
        SkRasterClip*   fRasterClip;//裁剪区域,若这个状态有独立裁剪区域,则指向内存(fRasterClip),否则继承上一个的。
        SkDrawFilter*   fFilter;
        DeviceCM* fLayer;//这个状态所拥有的layer(需要在此MCRec销毁时回收)
        DeviceCM* fTopLayer;//这个状态下,所需要绘制的Layer链表。(这些Layer不一定属于此状态)
        ......
    };
    DeviceCM:图层链表,包装一个SkBaseDevice,附加一个位置偏移变化的矩阵(在saveLayer时指定的坐标)。


    (3)两重循环绘制
    研究Skia的人,一般来说都会被一开始的两重循环弄晕一会,比如drawRect的代码:


        LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)


        while (iter.next()) {
            iter.fDevice->drawRect(iter, r, looper.paint());
        }


        LOOPER_END


    先完全展开上面的代码:
    AutoDrawLooper  looper(this, paint, false, bounds);
    while (looper.next(type)) {
        SkDrawIter          iter(this);
        while (iter.next()) {
            iter.fDevice->drawRect(iter, r, looper.paint());
        }
    }


    第一重循环即 AutoDrawLooper,这个next实际上是做一个后处理,在存在 SkImageFilter 的情况下,先渲染到临时Layer上,再将这个Layer做Filter处理后画到当前device上。
    第二重循环是SkDrawIter,这个是绘制当前状态所依附的所有Layer。
    一般情况下,这两重循环都可以忽略,单纯当它是走下流程就好了。


    个人认为Skia在绘制入口SkCanvas的设计并不是很好,图层、矩阵与裁剪存在一起,导致渲染任务难以剥离,后面GPU渲染和延迟渲染的引入都让人感到有些生硬。


    2、SkDraw、SkBlitter
    这两个类在后续章节还会提到,这里只简单介绍:
    SkDraw是CPU绘图的实现入口,主要任务是做渲染准备(形状确定、几何变换、字体解析、构建图像Shader等)。
    SkBlitter 不是单独的一个类,指代了一系列根据图像格式、是否包含Shader等区分出来的一系列子类。
    这一族类执行大块头的渲染任务,把像素绘制上去。





    三、渲染框架设计思想分析
    1、指令层与实现层分离
    SkCanvas不直接执行渲染,由SkBaseDevice根据设备类型,选择渲染方法。这样虽然是同一套API,但可以用作GPU绘图、pdf绘制、存储显示列表等各种功能。在API集上做优化,避免冗余绘制,也因此成为可能(注:这个google虽然在尝试,但目前看来没有明显效果,实现起来确实也很困难)。
    2、图=形+色的设计思想
    由SkDraw和SkScan类中控制绘制的形,由SkBlitter和SkShader控制绘制的色,将绘图操作分解为形状与色彩两部分,这一点和OpenGL的顶点变换——光栅——片断着色管线相似,非常有利于扩展,各种2D图元的绘制基本上就完全支持了。
    3、性能调优集中化
    将耗时的函数抽象都抽象为proc,由一个工厂制造,便于集中对这一系列函数做优化。

    此篇讲Skia绘制图片的流程,在下一篇讲图像采样原理、混合和抖动技术
    1、API用法
    (1)drawBitmap
    void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint = NULL);
    将bitmap画到x,y的位置(这本身是一个平移,需要和SkCanvas中的矩阵状态叠加)。
    (2)drawBitmapRect 和 drawBitmapRectToRect
    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是AndroidL上为了gpu绘制效果而加上去的,在CPU绘制中不需要关注。
    (3)drawSprite
    void drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint);
    无视SkCanvas的矩阵状态,将bitmap平移到x,y的位置。
    (4)drawBitmapMatrix
    void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
    绘制的bitmap带有matrix的矩形变换,需要和SkCanvas的矩形变换叠加。
    (5)drawRect
    void drawRect(const SkRect& r, const SkPaint& paint);
    这个是最通用的方法,多用于需要加入额外效果的场景,比如需要绘制重复纹理。关于Tile的两个参数就是OpenGL纹理贴图中水平垂直方向上的边界处理模式。
    由这种用法,大家不难类推到非矩形图像绘制的方法,比如画圆角矩形图标、把方图片裁剪成一个圆等。
    下面是一个Demo程序

    #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;
    }


    (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.cpp drawBitmap 函数
    a、(bitmap.colorType() != kAlpha_8_SkColorType && just_translate(matrix, bitmap))
    kAlpha_8_SkColorType 的图像只有一个通道alpha,按 drawMask 方式处理,将Paint中的颜色按图像的alpha预乘,叠加到目标区域上。
    just_translate表示matrix为一个平移矩阵,这时不涉及旋转缩放,bitmap的像素点和SkCanvas绑定的dstBitmap的像素点此时存在连续的一一对齐关系。
    b、clipHandlesSprite(*fRC, ix, iy, bitmap))
    这个条件是指当前SkCanvas的裁剪区域不需要考虑抗锯齿或者完全包含了bitmap的渲染区域。SkCanvas的任何渲染都必须在裁剪区域之内,因此如果图像跨越了裁剪区域边界而且裁剪区域需要考虑抗锯齿,在边界上需要做特殊处理。
    注:裁剪区域的设置API
    void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA)
    doAA即是否在r的边界非整数时考虑抗锯齿。
    满足条件,创建SkSpriteBlitter,由SkScan::FillIRect按每个裁剪区域调用SkSpriteBlitter的blitRect。
    这种情况下可以直接做颜色转换和透明度合成渲染过去,不需要做抗锯齿和图像插值,也就不需要走取样——混合流程,性能是最高的。


    满足条件后通过ChooseSprite去选一个SkSpriteBlitter
    详细代码见 external/skia/src/core/SkBlitter_Sprite.cpp 中的 ChooseSprite 函数。
    这函数实际上很多场景都没覆盖到,因此很可能是选不到的,这时就开始转回drawRect流程。
    (3)创建BitmapShader
    在  SkAutoBitmapShaderInstall install(bitmap, paint); 这一句代码中,为paint创建了bitmapShader:
            fPaint.setShader(CreateBitmapShader(src, SkShader::kClamp_TileMode,
                                                SkShader::kClamp_TileMode,
                                                localMatrix, &fAllocator));
    然后就可以使用drawRect画图像了。


    (4)drawRect
    不能使用SkSpriteBlitter的场景,走drawRect通用流程。
    这里有非常多的分支,只讲绘制实矩形的情形。
    通过 SkAutoBlitterChoose -> SkBlitter::Choose,根据Canvas绑定的Bitmap像素模式,paint属性去选择blitter。
    绘制图片时paint有Shader(SkBitmapProcShader),因此是选的是带Shader的Blitter,比如适应ARGB格式的 SkARGB32_Shader_Blitter
    (5)SkScan
    在SkScan中,对每一个裁剪区域,将其与绘制的rect求交,然后渲染这个相交区域。此外,在需要时做抗锯齿。
    做抗锯齿的基本方法就是对浮点的坐标,按其离整数的偏离度给一个alpha权重,将颜色乘以此权重(减淡颜色)画上去。
    SkScan中在绘制矩形时,先用blitV绘制左右边界,再用blitAntiH绘制上下边界,中间大块的不需要考虑抗锯齿,因而用blitRect。
    (6)blitRect
    这一步先通过 Shader的shadeSpan方法取对应位置的像素,再将此像素通过SkBlitRow的proc叠加上去。
    如果不需要考虑混合模式,可以跳过proc。
    参考代码:external/skia/src/core/SkBlitter_ARGB32.cpp 中的blitRect
    (7)shadeSpan
    这里只考虑 SkBitmapProcShader 的shadeSpan,这主要是图像采样的方法。详细代码见 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坐标开始,渲染一行width个像素
    (2)blitV
    virtual void blitV(int x, int y, int height, SkAlpha alpha);
    从x,y开始,渲染一列height个像素,按alpha值对颜色做减淡处理
    (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);
    主要绘制文字时使用,以一个颜色乘上mash中的透明度,叠加。

    一、采样流程
    我们先看一个具体的blitRect实现。

    void SkARGB32_Shader_Blitter::blitRect(int x, int y, int width, int height) {
        SkASSERT(x >= 0 && y >= 0 &&
                 x + width <= fDevice.width() && y + height <= fDevice.height());
        uint32_t*          device = fDevice.getAddr32(x, y);
        size_t             deviceRB = fDevice.rowBytes();
        SkShader::Context* shaderContext = fShaderContext;
        SkPMColor*         span = fBuffer;
        if (fConstInY) {
            if (fShadeDirectlyIntoDevice) {
                // shade the first row directly into the device
                shaderContext->shadeSpan(x, y, device, width);
                span = device;
                while (--height > 0) {
                    device = (uint32_t*)((char*)device + deviceRB);
                    memcpy(device, span, width << 2);
                }
            } else {
                shaderContext->shadeSpan(x, y, span, width);
                SkXfermode* xfer = fXfermode;
                if (xfer) {
                    do {
                        xfer->xfer32(device, span, width, NULL);
                        y += 1;
                        device = (uint32_t*)((char*)device + deviceRB);
                    } while (--height > 0);
                } else {
                    SkBlitRow::Proc32 proc = fProc32;
                    do {
                        proc(device, span, width, 255);
                        y += 1;
                        device = (uint32_t*)((char*)device + deviceRB);
                    } while (--height > 0);
                }
            }
            return;
        }
        if (fShadeDirectlyIntoDevice) {
            void* ctx;
            SkShader::Context::ShadeProc shadeProc = shaderContext->asAShadeProc(&ctx);
            if (shadeProc) {
                do {
                    shadeProc(ctx, x, y, device, width);
                    y += 1;
                    device = (uint32_t*)((char*)device + deviceRB);
                } while (--height > 0);
            } else {
                do {
                    shaderContext->shadeSpan(x, y, device, width);
                    y += 1;
                    device = (uint32_t*)((char*)device + deviceRB);
                } while (--height > 0);
            }
        } else {
            SkXfermode* xfer = fXfermode;
            if (xfer) {
                do {
                    shaderContext->shadeSpan(x, y, span, width);
                    xfer->xfer32(device, span, width, NULL);
                    y += 1;
                    device = (uint32_t*)((char*)device + deviceRB);
                } while (--height > 0);
            } else {
                SkBlitRow::Proc32 proc = fProc32;
                do {
                    shaderContext->shadeSpan(x, y, span, width);
                    proc(device, span, width, 255);
                    y += 1;
                    device = (uint32_t*)((char*)device + deviceRB);
                } while (--height > 0);
            }
        }
    }

    其中shadeSpan用来将shader中x,y坐标处的值取n个到dst的buffer中。对于图像绘制时,它是 SkBitmapProcShader,这里是其实现:
    void SkBitmapProcShader::BitmapProcShaderContext::shadeSpan(int x, int y, SkPMColor dstC[],
                                                                int count) {
        const SkBitmapProcState& state = *fState;
        if (state.getShaderProc32()) {
            state.getShaderProc32()(state, x, y, dstC, count);
            return;
        }


        uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
        SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
        SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
        int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);


        SkASSERT(state.fBitmap->getPixels());
        SkASSERT(state.fBitmap->pixelRef() == NULL ||
                 state.fBitmap->pixelRef()->isLocked());


        for (;;) {
            int n = count;
            if (n > max) {
                n = max;
            }
            SkASSERT(n > 0 && n < BUF_MAX*2);
    #ifdef TEST_BUFFER_OVERRITE
            for (int i = 0; i < TEST_BUFFER_EXTRA; i++) {
                buffer[BUF_MAX + i] = TEST_PATTERN;
            }
    #endif
            mproc(state, buffer, n, x, y);
    #ifdef TEST_BUFFER_OVERRITE
            for (int j = 0; j < TEST_BUFFER_EXTRA; j++) {
                SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN);
            }
    #endif
            sproc(state, buffer, n, dstC);


            if ((count -= n) == 0) {
                break;
            }
            SkASSERT(count > 0);
            x += n;
            dstC += n;
        }
    }

    流程如下:
    1、存在 shaderProc,直接用
    2、计算一次能处理的像素数count
    3、mproc计算count个坐标,sproc根据坐标值去取色
    注意到之前三个函数指针:
    state.getShaderProc32
    mproc = state.getMatrixProc
    sproc = state.getShaderProc32
    这三个函数指针在一开始创建blitter时设定:

    SkBlitter::Choose -> SkShader::createContext -> SkBitmapProcShader::onCreateContext -> SkBitmapProcState::chooseProcs


    这是一个相当长的函数,它做的事情如下:
    1、(优化步骤)在大于SkPaint::kLow_FilterLevel的质量要求下,试图做预缩放。
    2、选择matrix函数:chooseMatrixProc。
    3、选择sample函数:
    (1)高质量:setBitmapFilterProcs
    (2)kLow_FilterLevel或kNone_FilterLevel:采取flags计算的方法,根据x,y变化矩阵情况和采样要求选择函数
    4、(优化步骤)在满足条件时,选取shader函数,此函数替代matrix和sample函数
    5、(优化步骤)platformProcs(),进一步选择优化版本的sample函数
    对于RGB565格式的目标,使用的是SkShader的 shadeSpan16 方法。shadeSpan16的代码逻辑类似,不再说明。

    bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
        SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());
        fBitmap = NULL;
        fInvMatrix = inv;
        fFilterLevel = paint.getFilterLevel();
        SkASSERT(NULL == fScaledCacheID);
        // possiblyScaleImage will look to see if it can rescale the image as a
        // preprocess; either by scaling up to the target size, or by selecting
        // a nearby mipmap level.  If it does, it will adjust the working
        // matrix as well as the working bitmap.  It may also adjust the filter
        // quality to avoid re-filtering an already perfectly scaled image.
        if (!this->possiblyScaleImage()) {
            if (!this->lockBaseBitmap()) {
                return false;
            }
        }
        // The above logic should have always assigned fBitmap, but in case it
        // didn't, we check for that now...
        // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)?
        if (NULL == fBitmap) {
            return false;
        }
        // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale,
        // so we downgrade to kLow (so the rest of the sniffing code can assume that)
        if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
            fFilterLevel = SkPaint::kLow_FilterLevel;
        }
        bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
        bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
                          SkShader::kClamp_TileMode == fTileModeY;
        if (!(clampClamp || trivialMatrix)) {
            fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
        }
        // Now that all possible changes to the matrix have taken place, check
        // to see if we're really close to a no-scale matrix.  If so, explicitly
        // set it to be so.  Subsequent code may inspect this matrix to choose
        // a faster path in this case.
        // This code will only execute if the matrix has some scale component;
        // if it's already pure translate then we won't do this inversion.
        if (matrix_only_scale_translate(fInvMatrix)) {
            SkMatrix forward;
            if (fInvMatrix.invert(&forward)) {
                if (clampClamp ? just_trans_clamp(forward, *fBitmap)
                                : just_trans_general(forward)) {
                    SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
                    SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
                    fInvMatrix.setTranslate(tx, ty);
                }
            }
        }
        fInvProc        = fInvMatrix.getMapXYProc();
        fInvType        = fInvMatrix.getType();
        fInvSx          = SkScalarToFixed(fInvMatrix.getScaleX());
        fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
        fInvKy          = SkScalarToFixed(fInvMatrix.getSkewY());
        fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());
        fAlphaScale = SkAlpha255To256(paint.getAlpha());
        fShaderProc32 = NULL;
        fShaderProc16 = NULL;
        fSampleProc32 = NULL;
        fSampleProc16 = NULL;
        // recompute the triviality of the matrix here because we may have
        // changed it!


        trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;


        if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
            // If this is still set, that means we wanted HQ sampling
            // but couldn't do it as a preprocess.  Let's try to install
            // the scanline version of the HQ sampler.  If that process fails,
            // downgrade to bilerp.


            // NOTE: Might need to be careful here in the future when we want
            // to have the platform proc have a shot at this; it's possible that
            // the chooseBitmapFilterProc will fail to install a shader but a
            // platform-specific one might succeed, so it might be premature here
            // to fall back to bilerp.  This needs thought.


            if (!this->setBitmapFilterProcs()) {
                fFilterLevel = SkPaint::kLow_FilterLevel;
            }
        }


        if (SkPaint::kLow_FilterLevel == fFilterLevel) {
            // Only try bilerp if the matrix is "interesting" and
            // the image has a suitable size.


            if (fInvType <= SkMatrix::kTranslate_Mask ||
                    !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
                fFilterLevel = SkPaint::kNone_FilterLevel;
            }
        }


        // At this point, we know exactly what kind of sampling the per-scanline
        // shader will perform.


        fMatrixProc = this->chooseMatrixProc(trivialMatrix);
        // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
        if (NULL == fMatrixProc) {
            return false;
        }


        ///


        // No need to do this if we're doing HQ sampling; if filter quality is
        // still set to HQ by the time we get here, then we must have installed
        // the shader procs above and can skip all this.


        if (fFilterLevel < SkPaint::kHigh_FilterLevel) {


            int index = 0;
            if (fAlphaScale < 256) {  // note: this distinction is not used for D16
                index |= 1;
            }
            if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
                index |= 2;
            }
            if (fFilterLevel > SkPaint::kNone_FilterLevel) {
                index |= 4;
            }
            // bits 3,4,5 encoding the source bitmap format
            switch (fBitmap->colorType()) {
                case kN32_SkColorType:
                    index |= 0;
                    break;
                case kRGB_565_SkColorType:
                    index |= 8;
                    break;
                case kIndex_8_SkColorType:
                    index |= 16;
                    break;
                case kARGB_4444_SkColorType:
                    index |= 24;
                    break;
                case kAlpha_8_SkColorType:
                    index |= 32;
                    fPaintPMColor = SkPreMultiplyColor(paint.getColor());
                    break;
                default:
                    // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
                    return false;
            }


        #if !SK_ARM_NEON_IS_ALWAYS
            static const SampleProc32 gSkBitmapProcStateSample32[] = {
                S32_opaque_D32_nofilter_DXDY,
                S32_alpha_D32_nofilter_DXDY,
                S32_opaque_D32_nofilter_DX,
                S32_alpha_D32_nofilter_DX,
                S32_opaque_D32_filter_DXDY,
                S32_alpha_D32_filter_DXDY,
                S32_opaque_D32_filter_DX,
                S32_alpha_D32_filter_DX,


                S16_opaque_D32_nofilter_DXDY,
                S16_alpha_D32_nofilter_DXDY,
                S16_opaque_D32_nofilter_DX,
                S16_alpha_D32_nofilter_DX,
                S16_opaque_D32_filter_DXDY,
                S16_alpha_D32_filter_DXDY,
                S16_opaque_D32_filter_DX,
                S16_alpha_D32_filter_DX,


                SI8_opaque_D32_nofilter_DXDY,
                SI8_alpha_D32_nofilter_DXDY,
                SI8_opaque_D32_nofilter_DX,
                SI8_alpha_D32_nofilter_DX,
                SI8_opaque_D32_filter_DXDY,
                SI8_alpha_D32_filter_DXDY,
                SI8_opaque_D32_filter_DX,
                SI8_alpha_D32_filter_DX,


                S4444_opaque_D32_nofilter_DXDY,
                S4444_alpha_D32_nofilter_DXDY,
                S4444_opaque_D32_nofilter_DX,
                S4444_alpha_D32_nofilter_DX,
                S4444_opaque_D32_filter_DXDY,
                S4444_alpha_D32_filter_DXDY,
                S4444_opaque_D32_filter_DX,
                S4444_alpha_D32_filter_DX,


                // A8 treats alpha/opaque the same (equally efficient)
                SA8_alpha_D32_nofilter_DXDY,
                SA8_alpha_D32_nofilter_DXDY,
                SA8_alpha_D32_nofilter_DX,
                SA8_alpha_D32_nofilter_DX,
                SA8_alpha_D32_filter_DXDY,
                SA8_alpha_D32_filter_DXDY,
                SA8_alpha_D32_filter_DX,
                SA8_alpha_D32_filter_DX
            };


            static const SampleProc16 gSkBitmapProcStateSample16[] = {
                S32_D16_nofilter_DXDY,
                S32_D16_nofilter_DX,
                S32_D16_filter_DXDY,
                S32_D16_filter_DX,


                S16_D16_nofilter_DXDY,
                S16_D16_nofilter_DX,
                S16_D16_filter_DXDY,
                S16_D16_filter_DX,


                SI8_D16_nofilter_DXDY,
                SI8_D16_nofilter_DX,
                SI8_D16_filter_DXDY,
                SI8_D16_filter_DX,


                // Don't support 4444 -> 565
                NULL, NULL, NULL, NULL,
                // Don't support A8 -> 565
                NULL, NULL, NULL, NULL
            };
        #endif


            fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
            index >>= 1;    // shift away any opaque/alpha distinction
            fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];


            // our special-case shaderprocs
            if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
                if (clampClamp) {
                    fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
                } else if (SkShader::kRepeat_TileMode == fTileModeX &&
                           SkShader::kRepeat_TileMode == fTileModeY) {
                    fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
                }
            } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
                fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
            }


            if (NULL == fShaderProc32) {
                fShaderProc32 = this->chooseShaderProc32();
            }
        }
        // see if our platform has any accelerated overrides
        this->platformProcs();
        return true;
    }

    二、MatrixProc和SampleProc
    MatrixProc的使命是生成坐标集。SampleProc则根据坐标集取像素,采样合成
    我们先倒过来看 sampleProc 看这个坐标集是怎么使用的:
    nofilter_dx系列:

    void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
            const uint32_t* SK_RESTRICT xy,
            int count, DSTTYPE* SK_RESTRICT colors) {
        for (int i = (count >> 1); i > 0; --i) {
            XY = *xy++;
            SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
                    (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
            src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
            *colors++ = RETURNDST(src);


            XY = *xy++;
            SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
                    (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
            src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
            *colors++ = RETURNDST(src);
        }
        if (count & 1) {
            XY = *xy++;
            SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
                    (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
            src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
            *colors++ = RETURNDST(src);
        }
    }

    这两个系列是直接取了x,y坐标处的图像像素
    filter_dx系列:

    filter_dxdy系列:

    void MAKENAME(_filter_DX)(const SkBitmapProcState& s,
                              const uint32_t* SK_RESTRICT xy,
                               int count, DSTTYPE* SK_RESTRICT colors) {
        SkASSERT(count > 0 && colors != NULL);
        SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
        SkDEBUGCODE(CHECKSTATE(s);)


    #ifdef PREAMBLE
        PREAMBLE(s);
    #endif
        const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
        size_t rb = s.fBitmap->rowBytes();
        unsigned subY;
        const SRCTYPE* SK_RESTRICT row0;
        const SRCTYPE* SK_RESTRICT row1;


        // setup row ptrs and update proc_table
        {
            uint32_t XY = *xy++;
            unsigned y0 = XY >> 14;
            row0 = (const SRCTYPE*)(srcAddr + (y0 >> 4) * rb);
            row1 = (const SRCTYPE*)(srcAddr + (XY & 0x3FFF) * rb);
            subY = y0 & 0xF;
        }


        do {
            uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
            unsigned x0 = XX >> 14;
            unsigned x1 = XX & 0x3FFF;
            unsigned subX = x0 & 0xF;
            x0 >>= 4;


            FILTER_PROC(subX, subY,
                        SRC_TO_FILTER(row0[x0]),
                        SRC_TO_FILTER(row0[x1]),
                        SRC_TO_FILTER(row1[x0]),
                        SRC_TO_FILTER(row1[x1]),
                        colors);
            colors += 1;


        } while (--count != 0);


    #ifdef POSTAMBLE
        POSTAMBLE(s);
    #endif
    }
    void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s,
                                const uint32_t* SK_RESTRICT xy,
                                int count, DSTTYPE* SK_RESTRICT colors) {
        SkASSERT(count > 0 && colors != NULL);
        SkASSERT(s.fFilterLevel != SkPaint::kNone_FilterLevel);
        SkDEBUGCODE(CHECKSTATE(s);)


    #ifdef PREAMBLE
            PREAMBLE(s);
    #endif
        const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
        size_t rb = s.fBitmap->rowBytes();


        do {
            uint32_t data = *xy++;
            unsigned y0 = data >> 14;
            unsigned y1 = data & 0x3FFF;
            unsigned subY = y0 & 0xF;
            y0 >>= 4;


            data = *xy++;
            unsigned x0 = data >> 14;
            unsigned x1 = data & 0x3FFF;
            unsigned subX = x0 & 0xF;
            x0 >>= 4;


            const SRCTYPE* SK_RESTRICT row0 = (const SRCTYPE*)(srcAddr + y0 * rb);
            const SRCTYPE* SK_RESTRICT row1 = (const SRCTYPE*)(srcAddr + y1 * rb);


            FILTER_PROC(subX, subY,
                        SRC_TO_FILTER(row0[x0]),
                        SRC_TO_FILTER(row0[x1]),
                        SRC_TO_FILTER(row1[x0]),
                        SRC_TO_FILTER(row1[x1]),
                        colors);
            colors += 1;
        } while (--count != 0);


    #ifdef POSTAMBLE
        POSTAMBLE(s);
    #endif
    }

    将四个相邻像素取出来之后,作Filter处理

    看晕了么,其实总结一下是这样:
    nofilter_dx,第一个32位数表示y,其余的32位数包含两个x坐标。
    nofilter_dxdy,用16位表示x,16位表示y。这种情况就是取的最近值,直接到x,y坐标处取值就可以了。
    filter_dxdy系列,每个32位数分别表示X和Y坐标(14:4:14),交错排列,中间的差值部分是相差的小数扩大16倍而得的近似整数。
    filter_dx系列,第一个数为Y坐标用14:4:14的方式存储,后面的数为X坐标,也用14:4:14的方式存储,前后为对应坐标,中间为放大16倍的距离,这个情况是一行之内y坐标相同(只做缩放或小数平移的情况),一样是作双线性插值。


    下面我们来看matrixproc的实现,

    先跟进 chooseMatrixProc的代码:

    SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
    //    test_int_tileprocs();
        // check for our special case when there is no scale/affine/perspective
        if (trivial_matrix) {
            SkASSERT(SkPaint::kNone_FilterLevel == fFilterLevel);
            fIntTileProcY = choose_int_tile_proc(fTileModeY);
            switch (fTileModeX) {
                case SkShader::kClamp_TileMode:
                    return clampx_nofilter_trans;
                case SkShader::kRepeat_TileMode:
                    return repeatx_nofilter_trans;
                case SkShader::kMirror_TileMode:
                    return mirrorx_nofilter_trans;
            }
        }


        int index = 0;
        if (fFilterLevel != SkPaint::kNone_FilterLevel) {
            index = 1;
        }
        if (fInvType & SkMatrix::kPerspective_Mask) {
            index += 4;
        } else if (fInvType & SkMatrix::kAffine_Mask) {
            index += 2;
        }


        if (SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY) {
            // clamp gets special version of filterOne
            fFilterOneX = SK_Fixed1;
            fFilterOneY = SK_Fixed1;
            return SK_ARM_NEON_WRAP(ClampX_ClampY_Procs)[index];
        }


        // all remaining procs use this form for filterOne
        fFilterOneX = SK_Fixed1 / fBitmap->width();
        fFilterOneY = SK_Fixed1 / fBitmap->height();


        if (SkShader::kRepeat_TileMode == fTileModeX && SkShader::kRepeat_TileMode == fTileModeY) {
            return SK_ARM_NEON_WRAP(RepeatX_RepeatY_Procs)[index];
        }


        fTileProcX = choose_tile_proc(fTileModeX);
        fTileProcY = choose_tile_proc(fTileModeY);
        fTileLowBitsProcX = choose_tile_lowbits_proc(fTileModeX);
        fTileLowBitsProcY = choose_tile_lowbits_proc(fTileModeY);
        return GeneralXY_Procs[index];
    }

    有些函数是找符号找不到的,我们注意到SkBitmapProcState.cpp 中包含了多次 SkBitmapProcState_matrix.h 头文件:

    #if !SK_ARM_NEON_IS_ALWAYS
    #define MAKENAME(suffix)        ClampX_ClampY ## suffix
    #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
    #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
    #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
    #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
    #define CHECK_FOR_DECAL
    #include "SkBitmapProcState_matrix.h"

    /*
     * Copyright 2011 Google Inc.
     *
     * Use of this source code is governed by a BSD-style license that can be
     * found in the LICENSE file.
     */


    #include "SkMath.h"
    #include "SkMathPriv.h"


    #define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
    #define AFFINE_FILTER_NAME      MAKENAME(_filter_affine)
    #define PERSP_FILTER_NAME       MAKENAME(_filter_persp)


    #define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
    #define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)


    #ifndef PREAMBLE
        #define PREAMBLE(state)
        #define PREAMBLE_PARAM_X
        #define PREAMBLE_PARAM_Y
        #define PREAMBLE_ARG_X
        #define PREAMBLE_ARG_Y
    #endif


    // declare functions externally to suppress warnings.
    void SCALE_FILTER_NAME(const SkBitmapProcState& s,
                                  uint32_t xy[], int count, int x, int y);
    void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
                                   uint32_t xy[], int count, int x, int y);
    void PERSP_FILTER_NAME(const SkBitmapProcState& s,
                                  uint32_t* SK_RESTRICT xy, int count,
                                  int x, int y);


    static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
                                              SkFixed one PREAMBLE_PARAM_Y) {
        unsigned i = TILEY_PROCF(f, max);
        i = (i << 4) | TILEY_LOW_BITS(f, max);
        return (i << 14) | (TILEY_PROCF((f + one), max));
    }


    static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
                                              SkFixed one PREAMBLE_PARAM_X) {
        unsigned i = TILEX_PROCF(f, max);
        i = (i << 4) | TILEX_LOW_BITS(f, max);
        return (i << 14) | (TILEX_PROCF((f + one), max));
    }


    void SCALE_FILTER_NAME(const SkBitmapProcState& s,
                                  uint32_t xy[], int count, int x, int y) {
        SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                                 SkMatrix::kScale_Mask)) == 0);
        SkASSERT(s.fInvKy == 0);


        PREAMBLE(s);


        const unsigned maxX = s.fBitmap->width() - 1;
        const SkFixed one = s.fFilterOneX;
        const SkFractionalInt dx = s.fInvSxFractionalInt;
        SkFractionalInt fx;


        {
            SkPoint pt;
            s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                      SkIntToScalar(y) + SK_ScalarHalf, &pt);
            const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
            const unsigned maxY = s.fBitmap->height() - 1;
            // compute our two Y values up front
            *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
            // now initialize fx
            fx = SkScalarToFractionalInt(pt.fX) - (SkFixedToFractionalInt(one) >> 1);
        }


    #ifdef CHECK_FOR_DECAL
        if (can_truncate_to_fixed_for_decal(fx, dx, count, maxX)) {
            decal_filter_scale(xy, SkFractionalIntToFixed(fx),
                               SkFractionalIntToFixed(dx), count);
        } else
    #endif
        {
            do {
                SkFixed fixedFx = SkFractionalIntToFixed(fx);
                *xy++ = PACK_FILTER_X_NAME(fixedFx, maxX, one PREAMBLE_ARG_X);
                fx += dx;
            } while (--count != 0);
        }
    }


    void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
                                   uint32_t xy[], int count, int x, int y) {
        SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
        SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                                 SkMatrix::kScale_Mask |
                                 SkMatrix::kAffine_Mask)) == 0);


        PREAMBLE(s);
        SkPoint srcPt;
        s.fInvProc(s.fInvMatrix,
                   SkIntToScalar(x) + SK_ScalarHalf,
                   SkIntToScalar(y) + SK_ScalarHalf, &srcPt);


        SkFixed oneX = s.fFilterOneX;
        SkFixed oneY = s.fFilterOneY;
        SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
        SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
        SkFixed dx = s.fInvSx;
        SkFixed dy = s.fInvKy;
        unsigned maxX = s.fBitmap->width() - 1;
        unsigned maxY = s.fBitmap->height() - 1;


        do {
            *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
            fy += dy;
            *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
            fx += dx;
        } while (--count != 0);
    }


    void PERSP_FILTER_NAME(const SkBitmapProcState& s,
                                  uint32_t* SK_RESTRICT xy, int count,
                                  int x, int y) {
        SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);


        PREAMBLE(s);
        unsigned maxX = s.fBitmap->width() - 1;
        unsigned maxY = s.fBitmap->height() - 1;
        SkFixed oneX = s.fFilterOneX;
        SkFixed oneY = s.fFilterOneY;


        SkPerspIter   iter(s.fInvMatrix,
                           SkIntToScalar(x) + SK_ScalarHalf,
                           SkIntToScalar(y) + SK_ScalarHalf, count);


        while ((count = iter.next()) != 0) {
            const SkFixed* SK_RESTRICT srcXY = iter.getXY();
            do {
                *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
                                           oneY PREAMBLE_ARG_Y);
                *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
                                           oneX PREAMBLE_ARG_X);
                srcXY += 2;
            } while (--count != 0);
        }
    }


    #undef MAKENAME
    #undef TILEX_PROCF
    #undef TILEY_PROCF
    #ifdef CHECK_FOR_DECAL
        #undef CHECK_FOR_DECAL
    #endif


    #undef SCALE_FILTER_NAME
    #undef AFFINE_FILTER_NAME
    #undef PERSP_FILTER_NAME


    #undef PREAMBLE
    #undef PREAMBLE_PARAM_X
    #undef PREAMBLE_PARAM_Y
    #undef PREAMBLE_ARG_X
    #undef PREAMBLE_ARG_Y


    #undef TILEX_LOW_BITS
    #undef TILEY_LOW_BITS

    然后我们就清楚了,这些函数名是用宏组合出来的。(神一般的代码。。。。。)
    怎么算坐标的不详述了,主要按原理去推就可以了,坐标计算有三种模式:CLAMP(越界时限制在边界)、REPEAT(越界时从开头取起)、MIRROR(越界时取样方向倒转去取)。
    sampleProc函数也是类似的方法组合出来的,不详述。



    三、高级插值算法
    双线性插值虽然在一般情况下够用了,但在放大图片时,效果还是不够好。需要更好的效果,可以用高级插值算法,代价是性能的大幅消耗。
    高级插值算法目前在Android的Java代码处是走不进去的,不知道chromium是否用到。
    几个要点:
    1、在 setBitmapFilterProcs 时判断高级插值是否支持,若支持,设置 shaderProc 为 highQualityFilter32/highQualityFilter16(也就是独立计算坐标和采样像素)
    2、highQualityFilter先通过变换矩阵计算原始点。
    3、highQualityFilter根据 SkBitmapFilter  的采样窗口,将这个窗口中的所有点按其与原始点矩离,查询对应权重值,然后相加,得到最终像素点。
    4、SkBitmapFilter 采用查表法去给出权重值,预计算由子类完成。
    5、目前Skia库用的是双三次插值 mitchell 法。

    SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which scanline bitmap filter to use [mitchell, lanczos, hamming, gaussian, triangle, box]");
    详细代码见 external/skia/src/core/SkBitmapFilter.cpp,尽量这部分代码几乎无用武之地,但里面的公式很值得借鉴,随便改改就能做成 glsl shader 用。

    看完这段代码,可以作不负责任的猜想:Skia设计之初,只考虑了近邻插值和双线性插值两种情况,因此采用这种模板方法,可以最小化代码量。而且MatrixProc和SampleProc可以后续分别作SIMD优化(Intel的SSE和ARM的Neon),以提高性能。
    但是对于线性插值,两步法(取值——采样)在算法实现上本来就不是最优的,后面又不得不引入shader函数,应对一些场景做优化。高阶插值无法在这个设计下实现,因此又像补丁一样打上去。


    四、总结
    看完这一部分代码,有三个感受。
    第一:绘张图片看上去一件简单的事,在渲染执行时,真心不容易,如果追求效果,还会有各种各样的花样。
    第二:在性能有要求的场景下,用模板真是灾难:函数改写时,遇到模板,就不得不重新定义函数,并替换之,弄得代码看上去一下子混乱不少。
    第三:从图像绘制这个角度上看,skia渲染性能虽然确实很好了,但远没有达到极限,仍然是有一定的优化空间的,如果这部分出现了性能问题,还是能做一定的优化的。关于Skia性能的讨论将放到介绍Skia系列的最后一章。
    第四:OpenGL+glsl确实是轻松且高效多了,软件渲染在复杂场景上性能很有限。







    展开全文
  • 首先你的Windows电脑需要能访问https://skia.org/和https://skia.googlesource.com/skia.git 在Windows上安装VisualStudio 2017 在Windows上安装Python v2.7.x(必须是v2系列),32位版本,例如python-2.7.18.msi ...

    环境准备

    • Windows7/10电脑需要能访问https://skia.org/https://skia.googlesource.com/skia.git
    • 在Windows上安装Visual Studio 2017
    • 在Windows上安装Python v2.7.x(必须是v2,不能使用v3),32位版本,例如python-2.7.18.msi。环境变量path需要添加python v2.7.x。如果电脑同时还装了v3,那么环境变量path需要把v2排在v3前面。

    • 在Windows终端输入命令"python --version"可以查看python2是否正确安装。

    • 在Windows上安装Git工具

     

    下载源码和依赖库

    截止目前,skia的版本是m84,https://skia.googlesource.com/skia.git/+refs

    我们使用Windows终端命令行,把源码下载:

    1、先设置git http代理:

    >git config --global http.proxy http://127.0.0.1:8000
    >git config --global https.proxy http://127.0.0.1:8000

    设置成功,代理信息会记录在C:\Users\firecat\.gitconfig

    2、再用git命令下载skia源码

    >git clone https://skia.googlesource.com/skia.git

    3、再下载依赖库
    >cd skia
    >python tools/git-sync-deps

     

    下载gn和ninja工具

    网上有好心人已经准备好了,直接下载就行了,然后解压到\skia\bin里面。环境变量path需要添加bin路径,例如我的路径是:

    F:\tmp-source\qt-hmi\skia\skia\bin

    下载链接:https://download.csdn.net/download/qq_35824650/12398406

    本人也备份了一份:https://download.csdn.net/download/libaineu2004/12432424

     

    源码编译

    • 参数说明

    is_debug=true 表示Debug

    is_component_build=true 表示动态库编译【推荐,否则是静态库编译,到时候生成的静态库运行时是MTd/MT】

    target_cpu="x86" 表示Win32平台

    ide=vs 表示使用VS IDE编译 【不推荐,容易出错】

     

    • x64位 Debug编译:

    >bin\gn gen out\Debug64
    >bin\gn args out\Debug64

    #这句话输入之后,会弹出一个记事本文件,请手动输入要编译的参数

    # Set build arguments here. See `gn help buildargs`.
    is_debug=true
    is_component_build=true

    保存记事本,然后继续终端命令

    >bin\gn gen out\Debug64
    >bin\ninja -C out\Debug64

     

    • x64位 Release编译:

    >bin\gn gen out\Release64
    >bin\gn args out\Release64
    # Set build arguments here. See `gn help buildargs`.
    is_debug=false
    is_component_build=true

    >bin\gn gen out\Release64
    >bin\ninja -C out\Release64

     

    • x86位 Debug编译:

    >bin\gn gen out\Debug86
    >bin\gn args out\Debug86
    # Set build arguments here. See `gn help buildargs`.
    is_debug=true
    is_component_build=true
    target_cpu="x86"

    >bin\gn gen out\Debug86

    #执行ninja之前,需要先打开路径\skia\out\Debug86的toolchain.ninja文件,手动删除所有

    "cmd /c C:/Program Files (x86)/Windows Kits/10/bin/SetEnv.cmd /x86 &&"的文本内容,

    #进入文件夹C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64

    拷贝以下3个文件到\skia\out\Debug86

    msobj140.dll
    mspdb140.dll
    mspdbcore.dll
    >bin\ninja -C out\Debug86

     

    • x86位 Release编译:

    >bin\gn gen out\Release86
    >bin\gn args out\Release86
    # Set build arguments here. See `gn help buildargs`.
    is_debug=false
    is_component_build=true
    target_cpu="x86"

    >bin\gn gen out\Release86

    #执行ninja之前,需要先打开路径\skia\out\Release86的toolchain.ninja文件,手动删除所有

    "cmd /c C:/Program Files (x86)/Windows Kits/10/bin/SetEnv.cmd /x86 &&"的文本内容,

    #进入文件夹C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64

    拷贝以下3个文件到\skia\out\Release86

    msobj140.dll
    mspdb140.dll
    mspdbcore.dll

    >bin\ninja -C out\Release86

     

    常用的gn命令

    bin\gn help args
    bin\gn help static_library
    bin\gn help buildargs
    bin\gn args --list out\Release64
    bin\gn args --list out\Debug64
    bin\gn args --list out\Release86
    bin\gn args --list out\Debug86
    bin\ninja -help

     

    之前编译失败的命令,也记录一下

    不建议使用VS的编译方式,容易失败

    >bin/gn gen out/Static --args="is_official_build=true skia_use_libpng=false skia_use_zlib=false skia_use_libjpeg_turbo=false skia_use_harfbuzz=false skia_use_libwebp=false skia_use_expat=false"
    
    vs要用管理员身份打开,不然编译的时候会报错,说什么没有访问权限
    x64 Debug
    >bin\gn gen out/vsx64d --ide=vs --args="is_debug=true is_official_build=false is_component_build=true"
    x64 Release
    >bin\gn gen out/vsx64 --ide=vs --args="is_debug=false is_official_build=false is_component_build=true"
    
    x86 Debug
    >bin\gn gen out/vsx86d --ide=vs --args="is_debug=true is_official_build=false is_component_build=true target_cpu=\"x86\""
    x86 Release
    >bin\gn gen out/vsx86 --ide=vs --args="is_debug=false is_official_build=false is_component_build=true target_cpu=\"x86\""

     

    最终编译好的库文件/头文件/案例

    https://download.csdn.net/download/libaineu2004/12436349

     

    参考文献

    https://www.jianshu.com/p/4225f4342d18 [图形引擎Skia之一]源码下载与编译(Windows)

    https://blog.drawoceans.com/codes/19/ 在Windows上使用VS2017编译64位和32位Skia

    https://blog.csdn.net/qq_35824650/article/details/105984644 国内正确编译V8和Skia

    https://gitee.com/QtSkia/QtSkia google的 skia渲染框架, 集成到Qt中

    https://gitee.com/QtSkia/QtSkia/blob/master/doc/Examples-zh.md SkCanvas是一个类似于QPainter的画笔,但性能和功能都比QPainter强大许多。

     

     

    展开全文
  • Skia引擎API整理介绍

    2019-07-05 00:20:28
    http://code.google.com/p/skia/ svn checkout http://skia.googlecode.com/svn/trunk/ skia-read-only Skia引擎重要类简介 (PS: 注意是简介了,观众不要要求太高,我也是在摸索中整理的文档) 1. SkCanvas 这...

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

        }

    }

    转载于:https://www.cnblogs.com/ququer/archive/2012/05/03/2480345.html

    展开全文
  • https://github.com/yuanming-hu/taichi中国人写的太极图形库 http://taichi.graphics/太极图形库,作者胡渊鸣 https://libcinder.org/矢量图形库,2D/3D https://github.com/cinder/Cinder ......

    绘图引擎简介

    Windows环境下二维绘图引擎有多种选择:GDI、GDI+、DirectDraw、Qt/QPainter、Agg、Cairo、skia、Direct2D、Direct3D、OpenGL、Vulkan等。

    GDI:微软原生的二维绘图引擎。

    优点:微软的全力支持,作为操作系统核心层效率方面不用担心,支持多种开发框架(含语言):WinSDK、MFC、Delphi等。

    缺点:不是面向C++对象组织的,使用起来较为繁琐;不支持反锯齿,不支持复杂的绘图效果(这个相对于GDI+而言)。

    GDI+:微软后来推出的二维绘图引擎。

    优点:微软的全力支持,支持多种开发框架(含语言):WinSDK、MFC、Delphi等,可以实现复杂的绘图效果,如反锯齿、路径画刷等;面向对象的架构,使用起来比较方便。

    缺点:绘图效率较GDI稍低,绘图交互性不如GDI(缺少GDI的支持位运算的绘图模式),开启反锯齿后效率不如Qt。如果不考虑绘图的效果,使用Win32 GDI函数直接绘图的效率大约是同样的GDI+的10倍以上。

    DirectDraw:从GDI、GDI+到Direct 2D的一个过渡产品,微软已明确表示不推荐使用。

    Qt:Qt的二维图形引擎是基于QPainter类的,绘图的效果取决于QPainter的设置。面向对象的方式组织,使用起来较为方便。

    Agg:C++编写的开源绘图引擎(基于GPL协议).AGG是C++编写的,因此,它不能被C语言很好地调用。由于其中使用了大量的现代标准C++语言的语法规则,包括模板、仿函数等处理 ,导致这样的库的学习过程将是痛苦的。AGG自从2006年之后就没有更新过。

    Cairo:C编写的开源绘图引擎(基于LGPL协议),大名鼎鼎的FireFox就是用这个绘图引擎的。Cairo是非常流行的开源2D图形渲染引擎库,它支持包括X-Windos,Win32,图像,pdf在内的各种输出设备。目前,Cairo已被广泛的使用在多个平台上来渲染图形界面,包括Firefox/Webkit-EFL/GTK+/Poppler/Qt等等。GTK+底层的绘图引擎就是使用Cairo。Qt的QPainter提供的抗锯齿效果没有cairo的好,在理想情况下,cairo的用户可以在打印机和屏幕上获得非常接近的输出效果。cairo 是用 C 编写的,但是为大多数常用的语言提供了绑定。选用 C 语言有助于创建新的绑定,同时在进行 C 语言调用时可以提供高性能。应该特别注意 Python 绑定,它支持快速原型开发,而且降低了学习 cairo 绘图 API 的门槛。 Cairo的绘图效率是接近GDI/GDIPlus的。经过优化算法,可以做到完全忽略绘图效率上的差别。此外,gtk不如qt流行,Qt支持cairo。

    Skia:Google的Android的绘图引擎。它是一个C++的开源2D向量图形处理函数库,包括字型、坐标转换、位图等等,相当于轻量级的Cairo(Cairo是矢量图形库)。

    angle:Google宣布了新的开源项目 ANGLE (全称 Almost Native Graphics Layer Engine),这个项目的目标是在 Direct X 9.0c API 的基础上实现一层 OpenGL ES 2.0 API中 的 Web GL 子集接口。在开发的早期,ANGLE 项目将使用 BSD 授权发布,而最终完成后,类似 Google Chrome 之类的浏览器在 Windows 平台上运行 WebGL 内容将不再依赖于任何的 OpenGL 驱动程序。

    Direct2D:微软在WindowsVista及之后的Windows版本推出的意在取代GDI、GDI+的二维绘图引擎,支持硬件加速。Direct 2D是微软在后XP时代开发的开发二维绘图引擎。微软出于兼容性的考虑还会继续对GDI、GDI+进行支持,但毫无疑问微软的策略是要Direct 2D取代GDI和GDI+的,因此在WindowsVista及其之后的Windows上进行二维绘图开发,建议是直接使用Direct2D。Direct 2D支持硬件加速,在绘图效率应有一定程度的提升。

    Direct3D:微软开发的3D绘图引擎。

    OpenGL:SGI开发的3D绘图引擎。OpenGL的优势是三维绘图,不建议用来二维绘图,因为OpenGL在二维一些操作并不合适,如二维中的点、线捕捉、自定义图例的添加、打印的支持等等。

    Vulkan:Vulkan是一个跨平台的2D和3D绘图应用程序接口。同 OpenGL一样,Vulkan也由 Khronos 集团开发。它是 AMD Mantle 的后续版本,继承了前者强大的低开销架构,使软件开发人员能够全面获取 RadeonGPU 与多核 CPU 的性能、效率和功能。Linux上Vulkan是OpenGL的指定接班人。

     

    OpenGL

    opengl.org:OpenGL官方网站。
    OpenGL registry:包含OpenGL各版本的规范和扩展。

    《OpenGL超级宝典》中的例子使用了glew,freeglut以及作者自己开发的GLTools这三个库

    • glew

    http://glew.sourceforge.net/ The OpenGL Extension Wrangler Library

    GLEW是一个跨平台的OpenGL的扩展库。进入官网,点击Binaries后面的链接,可以下载msvc已编译的库文件,含32位和64位。

    https://sourceforge.net/projects/glew/files/

    https://github.com/nigels-com/glew

    • freeglut

    FreeGLUT is a free-software/open-source alternative to the OpenGL Utility Toolkit (GLUT) library。

    http://freeglut.sourceforge.net/index.php

    https://www.transmissionzero.co.uk/software/freeglut-devel/ 这个网站可以下载msvc已编译的库文件,含32位和64位。

    • GLTools

    A set of tools to make working with OpenGL easier

    https://github.com/HazimGazov/GLTools

    http://starstonesoftware.com/

    • glfw

    https://www.glfw.org/download.html A multi-platform library for OpenGL

    https://github.com/glfw/glfw

    https://github.com/glfw/glfw/releases

    • glad

    https://github.com/Dav1dde/glad

    https://glad.dav1d.de/

    • CG Internals

    https://www.cginternals.com/en/  Computer Graphics Internals

    https://github.com/cginternals

    https://github.com/cginternals/cmake-init Template for reliable, cross-platform C++ project setup using cmake

    https://glbinding.org/ A C++ binding for the OpenGL API

    https://globjects.org/ C++ library strictly wrapping OpenGL objects

    • OpenGL binding

    https://github.com/JoeyDeVries/Cell ++ https://github.com/assimp/assimp 这两个要配套使用

    https://github.com/luca-piccioni/OpenGL.Net

    https://github.com/moderngl/moderngl

    • 其它

    https://github.com/wjakob/nanogui Minimalistic GUI library for OpenGL

    https://github.com/NVIDIAGameWorks/GraphicsSamples

    https://github.com/assimp/assimp 模型导入

    • OpenGL学习教程

    https://learnopengl-cn.github.io/ ++ https://learnopengl.com/

    https://github.com/JoeyDeVries/LearnOpenGL ++ https://github.com/JoeyDeVries/Cell

    http://www.opengl-tutorial.org/ ++ https://github.com/opengl-tutorials/ogl

    • gult/freegult/glew/glfw/glad概念详解

    请访问我的另一篇博文《OpenGL之gult/freeglut/glew/glfw/glad的联系与区别

    https://libaineu2004.blog.csdn.net/article/details/105879521

     

    Vulkan

    Vulkan(昵称“火山”或者“福尔康”),采用跨平台设计,但最重要的贡献是大幅降低绘制命令开销(draw call overhead),改善多线程性能,渲染性能更快,背后的理念和DX12和Mantle都是一致的。另外,Vulkan还会统一桌面的OpenGL和移动平台的OpenGL ES,而且谷歌已经明确了Android将会支持Vulkan。Vulkan的最大任务不是竞争DirectX,而是取代OpenGL,所以重点要看和后者的对比。在高分辨率、高画质、需要GPU发挥的时候,Vulkan、OpenGL的速度基本差不多,但是随着分辨率的降低,CPU越来越重要,Vulkan逐渐体现了出来,尤其是看看GTX 980 Ti,最多可以领先OpenGL 33%之多!

    https://www.khronos.org/registry/vulkan/

    • Vulan学习教程

    https://learnvulkan.com/

    https://github.com/SaschaWillems/Vulkan

     

    Cairo

    https://www.cairographics.org/

    https://www.cairographics.org/samples/

    https://www.cairographics.org/download/

    https://www.cairographics.org/releases/ 源码下载

    https://www.cairographics.org/cairomm/ C++ API for cairo

    cairo的应用案例是scribus软件,使用C++/Qt GUI

    scribus-1.5.5\scribus\scpainter.cpp

    scribus-1.5.5\scribus\scpainterex_cairo.cpp

    scribus-1.5.5\scribus\scpainterex_ps2.cpp

    cairo通过ScPainter类构造函数来和Qt的QImage做衔接:

    ScPainter::ScPainter( QImage *target, int w, int h, double transparency, int blendmode )
    {
        m_image = target;
        ...
        cairo_surface_t *img = cairo_image_surface_create_for_data(m_image->bits(), CAIRO_FORMAT_ARGB32, w, h, w*4);
        cairo_surface_set_device_scale(img, m_image->devicePixelRatio(), m_image->devicePixelRatio());
    }

    scribus软件的详情见我的另一篇博客《Scribus v1.5.5源码编译,使用VS2017+Qt5.12.7环境

     

    Google 2D图形库Skia

    介绍Skia图形库之前,先来看看Windows平台下的两大绘图API,分别是GDI和GDI+。

    • GDI

    由于没有复杂的抗锯齿处理,绘图效率非常不错,但是同样的绘制质量不好。没有半透明的处理功能,在对界面要求不高而对性能要求比较高的应用程序里面使用比较多,比如股票软件。

    • GDI+

    GDI的升级版本,Windows独立提供了一个GDIPlus.dll的动态库,具有跨语言特性,C++,C#,VB都可以使用。 API封装性也比较好,支持丰富的半透明处理功能和文字处理特效。 具备抗锯齿绘制的能力。同样的也有其弊端,主要表现在:

    1. 绘制性能低下,连续绘制1000张大图片CPU基本就扛不住了
    2. 部分API存在bug,最典型的就是文字大小计算,计算不准确,在文字处理场景下存在一定的弊端。
    3. 没有特效的处理能力,比如模糊处理,发光处理,三维旋转效果,只提供了2D的选择和矩阵透视变形特效。

    所以使用GDI和GDI+在一些复杂的场景下使用非常受限。有人说可以用OpenGL和DirectX来处理,这两个API库需要实现大量的底层功能,比如文字栅格化等等,另外依赖显卡的驱动,对低配置计算机不是很友好。除了以上的选择之外Google的Skia的推出确实帮助我们解决了一部分问题,在此基础上我们做对应的扩展即可让我们实现复杂的绘制效果。

    • Skia

    Skia是一个C++的开源2D向量图形处理函数库(Cairo是一个矢量库),包括字型、坐标转换、位图等等,相当于轻量级的Cairo。目前主要用于Google的Android和Chrome平台,Skia搭配OpenGL/ES与特定的硬件特征,强化显示的效果。Skia需要的底层库有:freetype2,expat,tinyxml。

     https://skia.org/ 官方网址

    https://skia.googlesource.com/skia 官方源码下载

    https://skia.org/user/build 官方编译教程 

    https://github.com/google/skia 镜像源码仓库

    https://github.com/aseprite/aseprite ++ https://github.com/aseprite/skia/releases ++ \

    ++ https://github.com/aseprite/skia/actions 他人已编译好的库【推荐,版本新,静态库,MTd/MT运行时库】

    https://github.com/rust-skia/skia ++ https://github.com/rust-skia/skia-binaries/releases 他人已编译好的库【不推荐,没有d】

    https://github.com/mpsuzuki/skia-binaries/releases 他人已编译好的库,版本旧

    https://github.com/mono/SkiaSharp Skia C#版本

    https://libaineu2004.blog.csdn.net/article/details/105931395 我个人写的skia编译教程,VS2017【推荐】

    • Skia for Qt

    https://github.com/QtSkia/QtSkia  把skia集成到Qt中

    https://gitee.com/QtSkia/QtSkia

    https://github.com/jaredtao

    https://jaredtao.github.io/

    • 应用案例

    https://github.com/nitramr/Draftoola skia库的应用案例,C++/Qt【推荐】

     

    Google 3D图形库angle

    https://github.com/google/angle  ++ https://github.com/Microsoft/angle

     

    VTK

    https://vtk.org/

    Vtk,(visualization toolkit)是一个开源的免费软件系统,主要用于三维计算机图形学、图像处理和可视化。

    https://github.com/lorensen/VTKExamples

    https://lorensen.github.io/VTKExamples/site/Cxx/ 官方文档

    https://blog.csdn.net/webzhuce 阿兵-AI医疗 VKT中文教程

    https://gitee.com/yaoxin001/openBrowser 基于QT 一个开源的文件浏览器,使用了VTK

     

    OpenVG

    http://www.hygraphics.com/

    https://github.com/memononen/nanovg

    https://github.com/memononen/nanosvg

    gingkoVG是原生OpenVG的全功能实现,她是完全遵循OpenVG 1.0.1的Spec定义;其目标是为嵌入系统提供切实可以运行的低成本的OpenVG的实现,以使在嵌入系统上使用OpenVG成为可能;

    SVG是一种图像文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形。

     

    开源图像绘制和处理软件

    GIMP是跨平台的图像处理程序。GIMP 是 GNU 图像处理程序(GNU Image Manipulation Program)的缩写。包括几乎所有图象处理所需的功能,号称Linux下的Adobe PhotoShop。GTK+( GIMP Toolkit ) 是一套在 GIMP 的基础上发展而来的高级的、可伸缩的现代化、跨平台图形工具包,提供一整套完备的图形构件。

    KDE和GNOME是LINUX里最常用的图形界面操作环境。我们一般将GNOME和KDE两大阵营称为GNOME/GTK和 KDE/Qt。

    https://www.gimp.org/

    https://download.gimp.org/pub/gimp/

    https://download.gimp.org/pub/gimp/stable/

    https://download.gimp.org/pub/gimp/v2.10/windows/

     

    Inkscape是一款外国开发的开源矢量图形编辑软件,与Illustrator、Freehand、CorelDraw、Xara X 等其他软件相似。Inkscape是一套矢量图形编辑器,号称Linux下的CorelDraw。Inkscape is an open source 2d vector graphics editor.

    https://inkscape.org/

    https://gitlab.com/inkscape/inkscape C++/GTK+

     

    darktable:是一个开源摄影工作流程应用程序和RAW原生图像处理工具。darktable号称linux版的lightroom。Adobe Photoshop Lightroom是Adobe 研发的一款以后期制作为重点的图形工具软件,是当今数字拍摄工作流程中不可或缺的一部分。

    https://www.darktable.org/
    https://github.com/darktable-org/darktable  C++/GTK

     

    Krita 是一款自由、免费、开源的专业绘画软件。它由懂得画画的程序员们开发,目标是打造一款人人都用得起的数字绘画工具。适用于: 概念美术设计等

    https://krita.org/

    https://invent.kde.org/kde/krita/tree/master  C++/Qt,依赖KDE

    https://github.com/KDE/krita

     

    Kolourpaint是一款KDE下的全能画图应用,界面友好,支持如PNG, JPG, TGA, PNM, GIF, BMP, XPM 或 TIFF等等图片格式

    http://www.kolourpaint.org/

    https://github.com/KDE/kolourpaint  C++/Qt,但是依赖KDE

    https://kde.org/applications/graphics/org.kde.kolourpaint

    https://cgit.kde.org/kolourpaint.git/

     

    Karbon是一个矢量绘图应用程序,具有易于使用,高度可定制和可扩展的用户界面.

    https://calligra.org/karbon/

    https://github.com/KDE/calligra/tree/master/karbon   C++/Qt,但是依赖KDE

    https://cgit.kde.org/calligra.git

     

    https://github.com/drawpile/Drawpile ++ https://drawpile.net/ 

    Qt实现,但是依赖KDE,KF5 Extra CMake Modules

     

    RainyNite studio,依赖boost,KDE,KF5ItemModels

    https://github.com/caryoscelus/rainynite-studio

     

    https://github.com/ksnip/ksnip

    Ksnip是基于Qt的跨平台屏幕截图工具,可为屏幕截图提供许多注释功能。

     

    Paint.NET是Windows 平台上的一个图像和照片处理软件,早期定位于MSPaint(Windows画图板)的免费替代软件,支持图层、通道、无限制的历史记录、特效和许多实用工具,在3.3版本之前开源,之后由于种种原因放弃开源。Paint.NET v3.36是最终的开源版本。C#语言编写。

    https://www.getpaint.net/ 官网,新版本exe下载

    http://www.oldversion.cn/windows/paint-net/ 老版本exe下载

    https://github.com/wangdeshui/paint.net

    https://download.csdn.net/download/libaineu2004/12309098 我个人整理的源码,可以在VS2017编译通过

     

    Pinta:Simple Gtk# Paint Program

    Pinta is a Gtk# clone of Paint.Net 3.0。Pinta是一个受PaintNET启发的开放源代码位图形编辑软件。它使用Gtk#、cairo和有些PaintNET的源代码。

    https://pinta-project.com/pintaproject/pinta/

    https://github.com/PintaProject/Pinta

     

    MyPaint

    http://mypaint.org/ MyPaint是一个绘画涂鸦软件,python,GTK写的

    https://github.com/mypaint/mypaint 主要是配合数位绘图板使用,软件内置了易于使用的笔刷。

     

    Pencil2D:2D动画制作软件

    https://www.pencil2d.org/ Qt

    https://github.com/pencil2d/pencil

     

    https://github.com/egan2015/qdraw

    画图软件,基于QGraphicsView实现,可画直线,矩形,椭圆,圆角矩形,不规则多边形,曲线等等基本功能,并且可编辑这些图形,代码封装完毕,可简单移植到项目中使用。

     

    https://github.com/douzhongqiang/EasyCanvas

    https://blog.csdn.net/douzhq/article/details/105210133 第一版的博文

    基于Qt QGraphicsView的简易画图软件,作者https://blog.csdn.net/douzhq

     

    https://github.com/kanryu/quickviewer

    适用于Windows,Mac和Linux的图像/漫画查看器应用程序,可以非常快速地显示图像。


    https://github.com/jurplel/qView

    实用且最小的图像查看器

     

    https://github.com/ArsMasiuk/qvge

    把这句话注释掉,不然编译失败
    void qvgeMainWindow::updateFileAssociations()
    {
    #if defined Q_OS_WIN32
        //CPlatformWin32::registerFileType("qvge.xgr", "QVGE native graph document", ".xgr", 0);//firecat
    

     

    https://github.com/OpenOrienteering/mapper

     

    https://github.com/SillyLossy/QtPaint

    Screenshot with Gnash logo

     

    https://github.com/zsnjuts/PaintWorks 图形学绘图系统Qt版(OpenGL绘制)

    screenshot.png

     

    https://github.com/omiddavoodi/QtPaint

     

    https://github.com/smay1613/Qt-Paint

     

    https://github.com/bruceoutdoors/DrawingApp Qt

     

    https://github.com/Gr1N/EasyPaint Qt

    https://www.linux-apps.com/content/show.php?content=140877

     

    https://github.com/mm-project/qt_painter

     

    LogoSpeech Studio

    https://github.com/mohabouje/logospeech-studio

     

    Crimm Imageshop 2.3,软件经过UPX压缩,占用空间很小。UPX (the Ultimate Packer for eXecutables)是一款先进的可执行程序文件压缩器,压缩过的可执行文件体积缩小50%-70% 

    https://www.cnblogs.com/Imageshop/p/3308782.html  

     

    https://photodemon.org/

    https://github.com/tannerhelland/PhotoDemon 不是Qt写的

     

    其它开源图形库

    https://github.com/yuanming-hu/taichi 中国人写的太极图形库

    http://taichi.graphics/ 太极图形库,作者胡渊鸣

    https://libcinder.org/ 矢量图形库,2D/3D
    https://github.com/cinder/Cinder

    https://github.com/FortAwesome/Font-Awesome

    https://github.com/drawpile/Drawpile

    https://easyx.cn/ 国产绘图库for VC++

     

    展开全文
  • skia简介

    千次阅读 2013-08-14 14:27:04
    【原创文章,转载请保留或注明出处:】 概述 skia是个2D向量图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现。...自2005年Skia被Google收购后,一直相当神秘低调,直到2007年初,Skia
  • 这次之前需要编译过skia,怎么编译可以看https://blog.csdn.net/weixin_42001089/article/details/80320424接...生成结果应该如下:接下来就是要将skia 一些头文件添加进来了,这些头文件在skia/include,如我的具...
  • skia

    千次阅读 2019-06-30 18:21:16
    skia skia是个2D向量图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现。不仅用于Google Chrome浏览器,新兴的Android开放手机平台也采用skia作为绘图处理,搭配OpenGL/...
  • skia源码下载

    热门讨论 2020-07-21 09:57:45
    skia 2d绘图库,用于android,windows等跨平台。
  • skia实现2D绘制

    千次阅读 2019-01-16 20:22:19
    skia是谷歌的一个开源2D引擎,用来实现利用CPU实现2D图形绘制。 下面是老朽写的一个例程,实现功能如下: 1.窗口的创建 2.图片解码 3.在窗口的任意位置绘制指定大小和透明度的图片 4.绘制文字 因为对OpenGL...
  • Android 图形绘制(skia

    千次阅读 2018-11-14 19:33:26
    Android 图形绘制(skia
  • Skia API的简单应用

    万次阅读 热门讨论 2010-10-27 15:26:00
    Skia 绘图概述Skia是Google一个底层的图形、文本、图像、动画等多方面的图形库,是Android中图形系统的引擎。Skia作为第三方软件放在external目录下:external/skia/。skia的源文件及部分头文件都在src目录下,导出...
  • 在Windows下编译多种VS版本的Skia

    万次阅读 2016-04-29 21:19:39
    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/51250537 这几天刚改了改Duilib,之前为了让Duilib更好的支持透明异形窗体所以把Duilib...现在来说Skia是个很不错的渲染,之前我只是单独
  • Skia深入分析1——skia上下文

    千次阅读 2016-06-08 17:03:57
     断断续续跟Android的skia库打了两年交道,如今交接掉了,便写写关于skia的一些知识,也算了结一段职业生涯。 找了找网上关于skia的文章,基本上都过时了,讲得也不怎么深入。虽然Skia只是一个2D引擎,但其深度...
  • Android图形库Skia(四)-生成PDF

    千次阅读 2018-05-04 16:21:06
    Android图形库Skia(四)-生成PDF 本文主要记录使用skia库生成pdf文件的过程,其实skia并不仅仅能在Android系统中使用,在一般的嵌入式Linux系统上也可以使用的。标题就以以前的写法写了。本文基于Android图形库Skia...
  • Android Skia编译运行demo

    2015-10-23 06:57:13
    skia下载后在Android文件夹下面有一个示例程序(skia/platform_tools/android/examples/hello_skia_app)。我在eclipse里,按照工程里的README文件说明运行hello_skia_app工程。 但是: 1.导入Skia的头文件,在...
  • Android skia简单应用

    千次阅读 2013-08-14 14:36:30
    Android skia简单应用  很简单的Skia 2D图形库的调用。   一、Skia 2D图形库  Skia是Google一个底层的图形、图像、动画、SVG、文本等多方面的图形库,它是Android中图形系统的引擎。  
  • 那些编译skia跳过的坑

    千次阅读 2018-05-15 15:50:26
    最近在windows上面编译skia,自己属于小白的那种,遇到了种种问题,各种坑,在前辈的指导下终于搞定了,下面首先就我自己安装过程遇到的问题分享一下,最后介绍一下简单的编译过程。这是其官网...
  • Skia深入分析8——Skia的GPU绘图

    千次阅读 2017-04-28 21:59:23
    Skia的GPU绘图一、Skia-GPU概述在Android4.2到Android5.0的过程中,skia中开发较频繁的部分莫过于GPU加速部分和延迟渲染机制,尽管目前来看几乎没有用到,但后续很可能会在Frameworks层引入。 在Android上面,只...
  • 文字绘制主要包括编码转换(主要是中文)、字形解析(点线或image)和实际渲染三个步骤。在这个过程中,字形解析...在中文字较多,使用多种字体,绘制的样式(粗/斜体)有变化时,这个缓存会变得很大,因此Skia
  • Skia简介和图形案例 Overview Skia是一个开源的2D图形库,提供各种常用的API,并可在多种软硬件平台上运行。谷歌Chrome浏览器、Chrome OS、Android、火狐浏览器、火狐操作系统以及其它许多产品都使用它作为图形引擎...
1 2 3 4 5 ... 20
收藏数 7,474
精华内容 2,989
关键字:

skia