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