xref: /aosp_15_r20/external/skia/site/docs/user/api/skcanvas_creation.md (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "include/core/SkData.h"
27    #include "include/core/SkImage.h"
28    #include "include/core/SkStream.h"
29    #include "include/core/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 = SkPngEncoder::Encode(nullptr, img, {});
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 "include/core/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 "include/gpu/ganesh/GrDirectContext.h"
81    #include "include/gpu/ganesh/gl/GrGLInterface.h"
82    #include "include/gpu/ganesh/gl/GrGLInterface.h"
83    #include "include/core/SkData.h"
84    #include "include/core/SkImage.h"
85    #include "include/core/SkStream.h"
86    #include "include/core/SkSurface.h"
87
88    void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {
89        // You've already created your OpenGL context and bound it.
90        sk_sp<const GrGLInterface> interface = nullptr;
91        // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current
92        // context in a platform-specific way. Alternatively, you may create your own GrGLInterface
93        // and initialize it however you like to attach to an alternate OpenGL implementation or
94        // intercept Skia's OpenGL calls.
95        sk_sp<GrDirectContext> context = GrDirectContexts::MakeGL(interface);
96        SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);
97        sk_sp<SkSurface> gpuSurface(
98                SkSurface::MakeRenderTarget(context.get(), skgpu::Budgeted::kNo, info));
99        if (!gpuSurface) {
100            SkDebugf("SkSurface::MakeRenderTarget returned null\n");
101            return;
102        }
103        SkCanvas* gpuCanvas = gpuSurface->getCanvas();
104        draw(gpuCanvas);
105        sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());
106        if (!img) { return; }
107        // Must pass non-null context so the pixels can be read back and encoded.
108        sk_sp<SkData> png = SkPngEncoder::Encode(context.get(), img, {});
109        if (!png) { return; }
110        SkFILEWStream out(path);
111        (void)out.write(png->data(), png->size());
112    }
113
114## SkPDF
115
116---
117
118The SkPDF backend uses `SkDocument` instead of `SkSurface`, since a document
119must include multiple pages.
120
121<!--?prettify lang=cc?-->
122
123    #include "include/docs/SkPDFDocument.h"
124    #include "include/core/SkStream.h"
125    void skpdf(int width, int height,
126               void (*draw)(SkCanvas*),
127               const char* path) {
128        SkFILEWStream pdfStream(path);
129        auto pdfDoc = SkPDF::MakeDocument(&pdfStream);
130        SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width),
131                                                SkIntToScalar(height));
132        draw(pdfCanvas);
133        pdfDoc->close();
134    }
135
136## SkPicture
137
138---
139
140The SkPicture backend uses SkPictureRecorder instead of SkSurface.
141
142<!--?prettify lang=cc?-->
143
144    #include "include/core/SkPictureRecorder.h"
145    #include "include/core/SkPicture.h"
146    #include "include/core/SkStream.h"
147    void picture(int width, int height,
148                 void (*draw)(SkCanvas*),
149                 const char* path) {
150        SkPictureRecorder recorder;
151        SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
152                                                            SkIntToScalar(height));
153        draw(recordingCanvas);
154        sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
155        SkFILEWStream skpStream(path);
156        // Open SKP files with `viewer --skps PATH_TO_SKP --slide SKP_FILE`
157        picture->serialize(&skpStream);
158    }
159
160## NullCanvas
161
162---
163
164The null canvas is a canvas that ignores all drawing commands and does nothing.
165
166<!--?prettify lang=cc?-->
167
168    #include "include/utils/SkNullCanvas.h"
169    void null_canvas_example(int, int, void (*draw)(SkCanvas*), const char*) {
170        std::unique_ptr<SkCanvas> nullCanvas = SkMakeNullCanvas();
171        draw(nullCanvas.get());  // NoOp
172    }
173
174## SkXPS
175
176---
177
178The (_still experimental_) SkXPS canvas writes into an XPS document.
179
180<!--?prettify lang=cc?-->
181
182    #include "include/core/SkDocument.h"
183    #include "include/core/SkStream.h"
184    #ifdef SK_BUILD_FOR_WIN
185    void skxps(IXpsOMObjectFactory* factory;
186               int width, int height,
187               void (*draw)(SkCanvas*),
188               const char* path) {
189        SkFILEWStream xpsStream(path);
190        sk_sp<SkDocument> xpsDoc = SkDocument::MakeXPS(&pdfStream, factory);
191        SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width),
192                                                SkIntToScalar(height));
193        draw(xpsCanvas);
194        xpsDoc->close();
195    }
196    #endif
197
198## SkSVG
199
200---
201
202The (_still experimental_) SkSVG canvas writes into an SVG document.
203
204<!--?prettify lang=cc?-->
205
206    #include "include/core/SkStream.h"
207    #include "include/svg/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        SkRect bounds = SkRect::MakeIWH(width, height);
216        std::unique_ptr<SkCanvas> svgCanvas =
217            SkSVGCanvas::Make(bounds, xmlWriter.get());
218        draw(svgCanvas.get());
219    }
220
221## Example
222
223---
224
225To try this code out, make a
226[new unit test using instructions here](/docs/dev/testing/tests) and wrap these
227functions together:
228
229<!--?prettify lang=cc?-->
230
231    #include "include/core/SkCanvas.h"
232    #include "include/core/SkPath.h"
233    #include "tests/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        gl_example( 256, 256, example, "out_gpu.png"    );
257        skpdf(      256, 256, example, "out_skpdf.pdf"  );
258        picture(    256, 256, example, "out_picture.skp");
259    }
260