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