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