1SkCanvas Creation
2=========================
3
4First, read about [the SkCanvas API](skcanvas_overview).
5
6Skia has multiple backends which receive SkCanvas drawing commands,
7including:
8
9-   [Raster](#raster) - CPU-only.
10-   [GPU](#gpu) - Skia's GPU-accelerated backend.
11-   [SkPDF](#skpdf) - PDF document creation.
12-   [SkPicture](#skpicture) - Skia's display list format.
13-   [NullCanvas](#nullcanvas)  - Useful for testing only.
14-   [SkXPS](#skxps) - Experimental XPS backend.
15-   [SkSVG](#sksvg) - Experimental SVG backend.
16
17Each backend has a unique way of creating a SkCanvas.  This page gives
18an example for each:
19
20<span id="raster"></span>
21Raster
22------
23
24The raster backend draws to a block of memory. This memory can be
25managed by Skia or by the client.
26
27The recommended way of creating a canvas for the Raster and Ganesh
28backends is to use a `SkSurface`, which is an object that manages
29the memory into which the canvas commands are drawn.
30
31<!--?prettify lang=cc?-->
32
33    #include "SkData.h"
34    #include "SkImage.h"
35    #include "SkStream.h"
36    #include "SkSurface.h"
37    void raster(int width, int height,
38                void (*draw)(SkCanvas*),
39                const char* path) {
40        sk_sp<SkSurface> rasterSurface =
41                SkSurface::MakeRasterN32Premul(width, height);
42        SkCanvas* rasterCanvas = rasterSurface->getCanvas();
43        draw(rasterCanvas);
44        sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());
45        if (!img) { return; }
46        sk_sp<SkData> png(img->encode());
47        if (!png) { return; }
48        SkFILEWStream out(path);
49        (void)out.write(png->data(), png->size());
50    }
51
52Alternatively, we could have specified the memory for the surface
53explicitly, instead of asking Skia to manage it.
54
55<!--?prettify lang=cc?-->
56
57    #include <vector>
58    #include "SkSurface.h"
59    std::vector<char> raster_direct(int width, int height,
60                                    void (*draw)(SkCanvas*)) {
61        SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
62        size_t rowBytes = info.minRowBytes();
63        size_t size = info.getSafeSize(rowBytes);
64        std::vector<char> pixelMemory(size);  // allocate memory
65        sk_sp<SkSurface> surface =
66                SkSurface::MakeRasterDirect(
67                        info, &pixelMemory[0], rowBytes);
68        SkCanvas* canvas = surface->getCanvas();
69        draw(canvas);
70        return pixelMemory;
71    }
72
73<span id="gpu"></span>
74GPU
75------
76
77GPU Surfaces must have a `GrContext` object which manages the
78GPU context, and related caches for textures and fonts. GrContexts
79are matched one to one with OpenGL contexts or Vulkan devices. That is, all
80SkSurfaces that will be rendered to using the same OpenGL context or Vulkan
81device should share a GrContext. Skia does not create a OpenGL context or Vulkan
82device for you. In OpenGL mode it also assumes that the correct OpenGL context
83has been made current to the current thread when Skia calls are made.
84
85<!--?prettify lang=cc?-->
86
87    #include "GrContext.h"
88    #include "gl/GrGLInterface.h"
89    #include "SkData.h"
90    #include "SkImage.h"
91    #include "SkStream.h"
92    #include "SkSurface.h"
93
94    void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
95        // You've already created your OpenGL context and bound it.
96        const GrGLInterface* interface = nullptr;
97        // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
98        // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and
99        // initialize it however you like to attach to an alternate OpenGL implementation or intercept
100        // Skia's OpenGL calls.
101        sk_sp<GrContext> context = GrContext::MakeGL(interface);
102        SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
103        sk_sp<SkSurface> gpuSurface(
104                SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
105        if (!gpuSurface) {
106            SkDebugf("SkSurface::MakeRenderTarget returned null\n");
107            return;
108        }
109        SkCanvas* gpuCanvas = gpuSurface->getCanvas();
110        draw(gpuCanvas);
111        sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
112        if (!img) { return; }
113        sk_sp<SkData> png(img->encode());
114        if (!png) { return; }
115        SkFILEWStream out(path);
116        (void)out.write(png->data(), png->size());
117    }
118
119<span id="skpdf"></span>
120SkPDF
121-----
122
123The SkPDF backend uses `SkDocument` instead of `SkSurface`, since
124a document must include multiple pages.
125
126<!--?prettify lang=cc?-->
127
128    #include "SkPDFDocument.h"
129    #include "SkStream.h"
130    void skpdf(int width, int height,
131               void (*draw)(SkCanvas*),
132               const char* path) {
133        SkFILEWStream pdfStream(path);
134        auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
135        SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
136                                                SkIntToScalar(height));
137        draw(pdfCanvas);
138        pdfDoc->close();
139    }
140
141<span id="skpicture"></span>
142SkPicture
143---------
144
145The SkPicture backend uses SkPictureRecorder instead of SkSurface.
146
147<!--?prettify lang=cc?-->
148
149    #include "SkPictureRecorder.h"
150    #include "SkPicture.h"
151    #include "SkStream.h"
152    void picture(int width, int height,
153                 void (*draw)(SkCanvas*),
154                 const char* path) {
155        SkPictureRecorder recorder;
156        SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
157                                                            SkIntToScalar(height));
158        draw(recordingCanvas);
159        sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
160        SkFILEWStream skpStream(path);
161        // Open SKP files with `viewer --skps PATH_TO_SKP --slide SKP_FILE`
162        picture->serialize(&skpStream);
163    }
164
165<span id="nullcanvas"></span>
166NullCanvas
167----------
168
169The null canvas is a canvas that ignores all drawing commands and does
170nothing.
171
172<!--?prettify lang=cc?-->
173
174    #include "SkNullCanvas.h"
175    void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
176        std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
177        draw(nullCanvas.get());  // NoOp
178    }
179
180<span id="skxps"></span>
181SkXPS
182-----
183
184The (*still experimental*) SkXPS canvas writes into an XPS document.
185
186<!--?prettify lang=cc?-->
187
188    #include "SkDocument.h"
189    #include "SkStream.h"
190    #ifdef SK_BUILD_FOR_WIN
191    void skxps(IXpsOMObjectFactory* factory;
192               int width, int height,
193               void (*draw)(SkCanvas*),
194               const char* path) {
195        SkFILEWStream xpsStream(path);
196        sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
197        SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
198                                                SkIntToScalar(height));
199        draw(xpsCanvas);
200        xpsDoc->close();
201    }
202    #endif
203
204<span id="sksvg"></span>
205SkSVG
206-----
207
208The (*still experimental*) SkSVG canvas writes into an SVG document.
209
210<!--?prettify lang=cc?-->
211
212    #include "SkStream.h"
213    #include "SkSVGCanvas.h"
214    #include "SkXMLWriter.h"
215    void sksvg(int width, int height,
216               void (*draw)(SkCanvas*),
217               const char* path) {
218        SkFILEWStream svgStream(path);
219        std::unique_ptr<SkXMLWriter> xmlWriter(
220                new SkXMLStreamWriter(&svgStream));
221        SkRect bounds = SkRect::MakeIWH(width, height);
222        std::unique_ptr<SkCanvas> svgCanvas =
223            SkSVGCanvas::Make(bounds, xmlWriter.get());
224        draw(svgCanvas.get());
225    }
226
227<span id="example"></span>
228Example
229-------
230
231To try this code out, make a [new unit test using instructions
232here](/dev/testing/tests) and wrap these funtions together:
233
234<!--?prettify lang=cc?-->
235
236    #include "SkCanvas.h"
237    #include "SkPath.h"
238    #include "Test.h"
239    void example(SkCanvas* canvas) {
240        const SkScalar scale = 256.0f;
241        const SkScalar R = 0.45f * scale;
242        const SkScalar TAU = 6.2831853f;
243        SkPath path;
244        for (int i = 0; i < 5; ++i) {
245            SkScalar theta = 2 * i * TAU / 5;
246            if (i == 0) {
247                path.moveTo(R * cos(theta), R * sin(theta));
248            } else {
249                path.lineTo(R * cos(theta), R * sin(theta));
250            }
251        }
252        path.close();
253        SkPaint p;
254        p.setAntiAlias(true);
255        canvas->clear(SK_ColorWHITE);
256        canvas->translate(0.5f * scale, 0.5f * scale);
257        canvas->drawPath(path, p);
258    }
259    DEF_TEST(FourBackends, r) {
260        raster(     256, 256, example, "out_raster.png" );
261        gl_example( 256, 256, example, "out_gpu.png"    );
262        skpdf(      256, 256, example, "out_skpdf.pdf"  );
263        picture(    256, 256, example, "out_picture.skp");
264    }
265