2312skia,16画布

创建SkCanvas

首先,阅读SkCanvasAPI概述.
Skia有多个接收SkCanvas绘图命令的后端.每个后端都有创建SkCanvas的独特方式.本页给出了每个示例:

光栅化

光栅化后端将绘画到可由Skia客户管理的内存块.
推荐用管理画布命令要绘画内存对象的SkSurfaceRasterGanesh后端创建画布.


#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkStream.h"
#include "include/core/SkSurface.h"
void raster(int width, int height, void (*draw)(SkCanvas*), const char* path) {
    sk_sp<SkSurface> rasterSurface = SkSurface::MakeRasterN32Premul(width, height);
    SkCanvas* rasterCanvas = rasterSurface->getCanvas();
    draw(rasterCanvas);
    sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
    if (!img) { return; }
    sk_sp<SkData> png = SkPngEncoder::Encode(nullptr, img, {});
    if (!png) { return; }
    SkFILEWStream out(path);
    (void)out.write(png->data(), png->size());
}

或,可显式指定表面的内存,而不是让Skia管理它.

#include 
#include "include/core/SkSurface.h"
std::vector<char> raster_direct(int width, int height, void (*draw)(SkCanvas*)) {
    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
    size_t rowBytes = info.minRowBytes();
    size_t size = info.getSafeSize(rowBytes);
    std::vector<char> pixelMemory(size);  // 分配内存
    sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect( info, &pixelMemory[0], rowBytes);
    SkCanvas* canvas = surface->getCanvas();
    draw(canvas);
    return pixelMemory;
}

GPU

GPU表面必须有管理GPU环境及纹理和字体相关缓存的GrContext对象.GrContextsOpenGL环境或Vulkan设备一一匹配.

也即,使用相同的OpenGL环境或Vulkan设备渲染到的所有SkSurfaces都应共享一个GrContext.Skia不会为你创建OpenGL环境或Vulkan设备.
OpenGL模式下,还假定在调用Skia时,已为当前线程的当前环境设置了正确的OpenGL环境.

    #include "include/gpu/GrDirectContext.h"
    #include "include/gpu/gl/GrGLInterface.h"
    #include "include/gpu/ganesh/gl/GrGLInterface.h"
    #include "include/core/SkData.h"
    #include "include/core/SkImage.h"
    #include "include/core/SkStream.h"
    #include "include/core/SkSurface.h"

    void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
        // 已经创建了`OpenGL`上下文并绑定了它
        sk_sp<const GrGLInterface> interface = nullptr;
         //将`interface`保留为`null`会使`Skia`按特定平台方式,提取当前上下文的`OpenGL`函数的指针.或,可创建自己的`GrGLInterface`,并初化它,以附加到备用`OpenGL`实现或拦截`Skia`的`OpenGL`调用
        sk_sp<GrDirectContext> context = GrDirectContexts::MakeGL(interface);
        SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
        sk_sp<SkSurface> gpuSurface(
                SkSurface::MakeRenderTarget(context.get(), skgpu::Budgeted::kNo, info));
        if (!gpuSurface) {
            SkDebugf("SkSurface::MakeRenderTarget returned null\n");
            return;
        }
        SkCanvas* gpuCanvas = gpuSurface->getCanvas();
        draw(gpuCanvas);
        sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
        if (!img) { return; }
        // 必须传递非空上下文,以便可以读回和编码`像素`
        sk_sp<SkData> png = SkPngEncoder::Encode(context.get(), img, {});
        if (!png) { return; }
        SkFILEWStream out(path);
        (void)out.write(png->data(), png->size());
    }

SKPDF格式

因为文档必须包含多页,SkPDF后端使用SkDocument而不是SkSurface.

    #include "include/docs/SkPDFDocument.h"
    #include "include/core/SkStream.h"
    void skpdf(int width, int height, void (*draw)(SkCanvas*), const char* path) {
        SkFILEWStream pdfStream(path);
        auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
        SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
        draw(pdfCanvas);
        pdfDoc->close();
    }

SkPicture

SkPicture后端使用SkPictureRecorder而不是SkSurface.

    #include "include/core/SkPictureRecorder.h"
    #include "include/core/SkPicture.h"
    #include "include/core/SkStream.h"
    void picture(int width, int height, void (*draw)(SkCanvas*), const char* path) {
        SkPictureRecorder recorder;
        SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), SkIntToScalar(height));
        draw(recordingCanvas);
        sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
        SkFILEWStream skpStream(path);
        // 用`viewer --skps PATH_TO_SKP --slide SKP_FILE`,打开SKP文件
        picture->serialize(&skpStream);
    }

空画布

空画布是忽略所有绘图命令无操作的画布.

    #include "include/utils/SkNullCanvas.h"
    void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
        std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
        draw(nullCanvas.get());  // 闲着
    }

SkXPS

(仍在实验阶段)把SkXPS画布写入XPS文档.

    #include "include/core/SkDocument.h"
    #include "include/core/SkStream.h"
    #ifdef SK_BUILD_FOR_WIN
    void skxps(IXpsOMObjectFactory* factory; int width, int height, void (*draw)(SkCanvas*), const char* path) {
        SkFILEWStream xpsStream(path);
        sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
        SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width), SkIntToScalar(height));
        draw(xpsCanvas);
        xpsDoc->close();
    }
    #endif

SkSVG

(仍在实验阶段)把SkSVG画布写入SVG文档.

    #include "include/core/SkStream.h"
    #include "include/svg/SkSVGCanvas.h"
    #include "SkXMLWriter.h"
    void sksvg(int width, int height, void (*draw)(SkCanvas*), const char* path) {
        SkFILEWStream svgStream(path);
        std::unique_ptr<SkXMLWriter> xmlWriter( new SkXMLStreamWriter(&svgStream));
        SkRect bounds = SkRect::MakeIWH(width, height);
        std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(bounds, xmlWriter.get());
        draw(svgCanvas.get());
    }


要试用此代码,请使用新的单元测试,并把这些函数包装在一起:

    #include "include/core/SkCanvas.h"
    #include "include/core/SkPath.h"
    #include "tests/Test.h"
    void example(SkCanvas* canvas) {
        const SkScalar scale = 256.0f;
        const SkScalar R = 0.45f * scale;
        const SkScalar TAU = 6.2831853f;
        SkPath path;
        for (int i = 0; i < 5; ++i) {
            SkScalar theta = 2 * i * TAU / 5;
            if (i == 0) {
                path.moveTo(R * cos(theta), R * sin(theta));
            } else {
                path.lineTo(R * cos(theta), R * sin(theta));
            }
        }
        path.close();
        SkPaint p;
        p.setAntiAlias(true);
        canvas->clear(SK_ColorWHITE);
        canvas->translate(0.5f * scale, 0.5f * scale);
        canvas->drawPath(path, p);
    }
    DEF_TEST(FourBackends, r) {
        raster(     256, 256, example, "out_raster.png" );
        gl_example( 256, 256, example, "out_gpu.png"    );
        skpdf(      256, 256, example, "out_skpdf.pdf"  );
        picture(    256, 256, example, "out_picture.skp");
    }

画布概述

绘图环境

细节

SkCanvasSkia绘图环境.它知道直接在哪绘图(即屏幕外像素屏幕位置),并维护一堆矩阵和剪切.

但注意,与其他API(如postscript,cairoawt)中的类似环境不同,Skia不会在环境中存储其他(如颜色,笔大小)绘图属性.相反,在每次绘画调用中,通过SkPaint显式指定它们.

确切地说,画笔而不是画布,描述绘画颜色和风格.
首先,可能想要擦除整个画布.可绘画巨大矩形来完成,但有更简单方法.

    void draw(SkCanvas* canvas) {
        SkPaint paint;
        paint.setColor(SK_ColorWHITE);
        canvas->drawPaint(paint);
    }

这(当然,尊重当前剪切)指定绘画用的颜色或着色器(和xfermode),并填充整个画布.如果画笔中有个着色器,则它也会遵循画布上的当前矩阵(见SkShader).
如果(用可选的xfermode)只想绘画个颜色,你可直接调用drawColor(),这样就不必赋值画笔了.

    void draw(SkCanvas* canvas) {
        canvas->drawColor(SK_ColorWHITE);
    }

所有其他绘画API类似,都以画笔参数结尾.
某些调用中,传递画笔指针,而不是引用.这时,paint参数可能为null.其他时候,必需要有paint参数.

画笔概览

每当你在Skia画笔某些内容时,想要指定颜色,或如何与背景混合,或使用的风格或字体,都可在画笔中指定这些属性.
SkCanvas不同,画笔不维护内部状态栈(即画笔上不保存/恢复).然而,画笔轻量的,因此客户可创建和维护多个用途不同的画笔对象.

画布状态中,分解出所有这些颜色和风格属性,并转换为(多个)画笔对象中,因为只需维护矩阵剪切设置栈,可更加高效的保存/恢复画布.

可显示三个不同画笔,每种画笔都按以不同的风格设置.现在,调用者可自由地混合这些画笔,可原样使用它们,也可在绘图过程中修改它们.

除了颜色,描边和文本值等简单属性外,画笔还支持特效.它是绘图管线不同方面的子类,引用画笔时(每个子类都按引用计数),调用它以覆盖绘图管线的某些部分.

如,要用渐变而不是单色画笔,请为画笔指定SkShader.
现在,使用该画笔的内容都使用调用MakeLinear()中指定的渐变画笔.返回的着色器对象是引用计数的.每当赋值着色器等特效对象画笔时,画笔都会增加引用计数.
为了平衡,上面赋值给画笔后,在着色器上马上调用unref().现在,画笔是该着色器的唯一"物主",当画笔出域或为其赋值另一个着色器(或null)时,它自动在着色器上调用unref().

有6种类型的特效可赋值给画笔:
1,SkPathEffect,在生成α掩码(如破折号)修改几何路径
2,SkRasterizer,合成自定义掩码图层(如阴影)
3,SkMaskFilter,在着色及绘画前修改α掩码(如模糊)
4,SkShader:如渐变(线性,径向,扫描),位图模式(夹,重复,镜像)
5,SkColorFilter,在混合前修改源颜色(如颜色矩阵)
6,SkBlendMode:如porter-duff传输模式,混合模式等
画笔还有SkTypeface的引用.字体表示来测量和绘画文本的特定字体风格.画笔不仅可绘画文本,还可测量文本.

    paint.measureText(...);
    paint.getTextBounds(...);
    paint.textToGlyphs(...);
    paint.getFontMetrics(...);

SkBlend模式

以下示例演示了Skia的所有标准混合模式.此例中,是有水平α渐变的纯洋红色,目标为有垂直α渐变的纯青色.

SkShader

定义了几个着色器(除了已提到的线性渐变):
1,位图着色器
2,径向渐变着色器
3,两点锥形渐变着色器
4,扫描渐变着色器
5,分形噪声着色器
6,湍流噪声着色器
7,合成着色器

SkMask过滤器

1,模糊掩码过滤器

SkColor过滤器

ColorMatrix颜色过滤器
颜色表颜色过滤器

SkPathEffect

1,SkPath2DPathEffect:用矩阵定义晶格,标记指定路径填充形状.
2,SkLine2DPathEffect:路径是要描边而不是要填充的直线路径的SkPath2DPathEffect的特例.
3,SkPath1DPathEffect:通过复制指定路径沿着画笔路径,创建类似破折号的特效.
4,SkCornerPathEffect:一个可变形尖角的特效(如圆角).
5,SkDashPathEffect:实现破折号的路径特效.
6,SkDiscretePathEffect:此路径特效把路径切成离散段,并随机替换它们.
7,SkComposePathEffect:先应用内部pathEffect,再外部pathEffect(即outer(inner(path)))的pathEffect特效.
8,SkSumPathEffect:按如下应用两个特效的pathEffect特效:

sequence (first(path) + second(path)).

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