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