1Creating SkCanvas Objects
2=========================
3
4First, read about [the SkCanvas API](skcanvas).
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 XPS 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(s->newImageSnapshot());
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    std::vector<char> raster_direct(int width, int height,
58                                    void(*draw)(SkCanvas*)) {
59        SkImageInfo info = SkImageInfo::MakeN32(width, height);
60        size_t rowBytes = info.minRowBytes();
61        size_t size = info.getSafeSize(rowBytes);
62        std::vector<char> pixelMemory(size);  // allocate memory
63        sk_sp<SkSurface> surface(
64                SkSurface::MakeRasterDirect(
65                        info, &pixelMemory[0], rowBytes));
66        SkCanvas* canvas = surface.getCanvas();
67        draw(canvas);
68        return std::move(pixelMemory);
69    }
70
71<span id="gpu"></span>
72GPU
73------
74
75GPU Surfaces must have a `GrContext` object which manages the
76GPU context, and related caches for textures and fonts. GrContexts
77are matched one to one with OpenGL contexts or Vulkan devices. That is, all
78SkSurfaces that will be rendered to using the same OpenGL context or Vulkan
79device should share a GrContext. Skia does not create a OpenGL context or Vulkan
80device for you. In OpenGL mode it also assumes that the correct OpenGL context
81has been made current to the current thread when Skia calls are made.
82
83<!--?prettify lang=cc?-->
84    #include "GrContext.h"
85    #include "gl/GrGLInterface.h"
86    #include "SkData.h"
87    #include "SkImage.h"
88    #include "SkStream.h"
89    #include "SkSurface.h"
90
91    void gl_example(int width, int height, void(*draw)(SkCanvas*), const char* path) {
92        // You've already created your OpenGL context and bound it.
93        const GrGLInterface* interface = nullptr;
94        // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
95        // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and
96        // initialize it however you like to attach to an alternate OpenGL implementation or intercept
97        // Skia's OpenGL calls.
98        GrContext* context = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) interface);
99        SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
100        sk_sp<SkSurface> gpuSurface(
101                SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));
102        if (!gpuSurface) {
103            SkDebugf("SkSurface::MakeRenderTarget returned null\n");
104            return;
105        }
106        SkCanvas* gpuCanvas = gpuSurface->getCanvas();
107        draw(gpuCanvas);
108        sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
109        if (!img) { return; }
110        sk_sp<SkData> png(img->encode());
111        if (!png) { return; }
112        SkFILEWStream out(path);
113        (void)out.write(png->data(), png->size());
114    }
115
116<span id="skpdf"></span>
117SkPDF
118-----
119
120The SkPDF backend uses `SkDocument` instead of `SkSurface`, since
121a document must include multiple pages.
122
123<!--?prettify lang=cc?-->
124
125    #include "SkDocument.h"
126    #include "SkStream.h"
127    void skpdf(int width, int height,
128               void(*draw)(SkCanvas*),
129               const char* path) {
130        SkFILEWStream pdfStream(path);
131        sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(&pdfStream));
132        SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
133                                                SkIntToScalar(height));
134        draw(pdfCanvas);
135        pdfDoc->close();
136    }
137
138<span id="skpicture"></span>
139SkPicture
140---------
141
142The SkPicture backend uses SkPictureRecorder instead of SkSurface.
143
144<!--?prettify lang=cc?-->
145
146    #include "SkPictureRecorder"
147    #include "SkPicture"
148    #include "SkStream.h"
149    void picture(int width, int height,
150                 void(*draw)(SkCanvas*),
151                 const char* path) {
152        SkPictureRecorder recorder;
153        SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
154                                                            SkIntToScalar(height));
155        draw(recordingCanvas);
156        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
157        SkFILEWStream skpStream(path);
158        // Open SKP files with `SampleApp --picture SKP_FILE`
159        picture->serialize(&skpStream);
160    }
161
162<span id="nullcanvas"></span>
163NullCanvas
164----------
165
166The null canvas is a canvas that ignores all drawing commands and does
167nothing.
168
169<!--?prettify lang=cc?-->
170
171    #include "SkNullCanvas.h"
172    void picture(int, int, void(*draw)(SkCanvas*), const char*) {
173        sk_sp<SkCanvas> nullCanvas(SkCreateNullCanvas());
174        draw(nullCanvas);  // NoOp
175    }
176
177<span id="skxps"></span>
178SkXPS
179-----
180
181The (*still experimental*) SkXPS canvas writes into an XPS document.
182
183<!--?prettify lang=cc?-->
184
185    #include "SkDocument.h"
186    #include "SkStream.h"
187    void skxps(int width, int height,
188               void(*draw)(SkCanvas*),
189               const char* path) {
190        SkFILEWStream xpsStream(path);
191        sk_sp<SkDocument> xpsDoc(SkDocument::MakeXPS(&pdfStream));
192        SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
193                                                SkIntToScalar(height));
194        draw(xpsCanvas);
195        xpsDoc->close();
196    }
197
198<span id="sksvg"></span>
199SkSVG
200-----
201
202The (*still experimental*) SkSVG canvas writes into an SVG document.
203
204<!--?prettify lang=cc?-->
205
206    #include "SkStream.h"
207    #include "SkSVGCanvas.h"
208    #include "SkXMLWriter.h"
209    void sksvg(int width, int height,
210               void(*draw)(SkCanvas*),
211               const char* path) {
212        SkFILEWStream svgStream(path);
213        std::unique_ptr<SkXMLWriter> xmlWriter(
214                new SkXMLStreamWriter(&svgStream));
215        sk_sp<SkCanvas> svgCanvas(SkSVGCanvas::Create(
216                SkRect::MakeWH(SkIntToScalar(src.size().width()),
217                               SkIntToScalar(src.size().height())),
218                xmlWriter));
219        draw(svgCanvas);
220    }
221
222<span id="example"></span>
223Example
224-------
225
226To try this code out, make a [new unit test using instructions
227here](/dev/testing/tests) and wrap these funtions together:
228
229<!--?prettify lang=cc?-->
230
231    #include "SkCanvas.h"
232    #include "SkPath.h"
233    #include "Test.h"
234    void example(SkCanvas* canvas) {
235        const SkScalar scale = 256.0f;
236        const SkScalar R = 0.45f * scale;
237        const SkScalar TAU = 6.2831853f;
238        SkPath path;
239        for (int i = 0; i < 5; ++i) {
240            SkScalar theta = 2 * i * TAU / 5;
241            if (i == 0) {
242                path.moveTo(R * cos(theta), R * sin(theta));
243            } else {
244                path.lineTo(R * cos(theta), R * sin(theta));
245            }
246        }
247        path.close();
248        SkPaint p;
249        p.setAntiAlias(true);
250        canvas->clear(SK_ColorWHITE);
251        canvas->translate(0.5f * scale, 0.5f * scale);
252        canvas->drawPath(path, p);
253    }
254    DEF_TEST(FourBackends, r) {
255        raster( 256, 256, example, "out_raster.png" );
256        ganesh( 256, 256, example, "out_ganesh.png" );
257        skpdf(  256, 256, example, "out_skpdf.pdf"  );
258        picture(256, 256, example, "out_picture.skp");
259    }
260