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