1Creating SkCanvas Objects 2========================= 3 4First, read about [the SkCanvas API](skcanvas). 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 XPS 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(s->newImageSnapshot()); 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 std::vector<char> raster_direct(int width, int height, 58 void(*draw)(SkCanvas*)) { 59 SkImageInfo info = SkImageInfo::MakeN32(width, height); 60 size_t rowBytes = info.minRowBytes(); 61 size_t size = info.getSafeSize(rowBytes); 62 std::vector<char> pixelMemory(size); // allocate memory 63 sk_sp<SkSurface> surface( 64 SkSurface::MakeRasterDirect( 65 info, &pixelMemory[0], rowBytes)); 66 SkCanvas* canvas = surface.getCanvas(); 67 draw(canvas); 68 return std::move(pixelMemory); 69 } 70 71<span id="gpu"></span> 72GPU 73------ 74 75GPU Surfaces must have a `GrContext` object which manages the 76GPU context, and related caches for textures and fonts. GrContexts 77are matched one to one with OpenGL contexts or Vulkan devices. That is, all 78SkSurfaces that will be rendered to using the same OpenGL context or Vulkan 79device should share a GrContext. Skia does not create a OpenGL context or Vulkan 80device for you. In OpenGL mode it also assumes that the correct OpenGL context 81has been made current to the current thread when Skia calls are made. 82 83<!--?prettify lang=cc?--> 84 #include "GrContext.h" 85 #include "gl/GrGLInterface.h" 86 #include "SkData.h" 87 #include "SkImage.h" 88 #include "SkStream.h" 89 #include "SkSurface.h" 90 91 void gl_example(int width, int height, void(*draw)(SkCanvas*), const char* path) { 92 // You've already created your OpenGL context and bound it. 93 const GrGLInterface* interface = nullptr; 94 // Leaving interface as null makes Skia extract pointers to OpenGL functions for the current 95 // context in a platform-specific way. Alternatively, you may create your own GrGLInterface and 96 // initialize it however you like to attach to an alternate OpenGL implementation or intercept 97 // Skia's OpenGL calls. 98 GrContext* context = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) interface); 99 SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height); 100 sk_sp<SkSurface> gpuSurface( 101 SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info)); 102 if (!gpuSurface) { 103 SkDebugf("SkSurface::MakeRenderTarget returned null\n"); 104 return; 105 } 106 SkCanvas* gpuCanvas = gpuSurface->getCanvas(); 107 draw(gpuCanvas); 108 sk_sp<SkImage> img(gpuSurface->makeImageSnapshot()); 109 if (!img) { return; } 110 sk_sp<SkData> png(img->encode()); 111 if (!png) { return; } 112 SkFILEWStream out(path); 113 (void)out.write(png->data(), png->size()); 114 } 115 116<span id="skpdf"></span> 117SkPDF 118----- 119 120The SkPDF backend uses `SkDocument` instead of `SkSurface`, since 121a document must include multiple pages. 122 123<!--?prettify lang=cc?--> 124 125 #include "SkDocument.h" 126 #include "SkStream.h" 127 void skpdf(int width, int height, 128 void(*draw)(SkCanvas*), 129 const char* path) { 130 SkFILEWStream pdfStream(path); 131 sk_sp<SkDocument> pdfDoc(SkDocument::MakePDF(&pdfStream)); 132 SkCanvas* pdfCanvas = pdfDoc->beginPage(SkIntToScalar(width), 133 SkIntToScalar(height)); 134 draw(pdfCanvas); 135 pdfDoc->close(); 136 } 137 138<span id="skpicture"></span> 139SkPicture 140--------- 141 142The SkPicture backend uses SkPictureRecorder instead of SkSurface. 143 144<!--?prettify lang=cc?--> 145 146 #include "SkPictureRecorder" 147 #include "SkPicture" 148 #include "SkStream.h" 149 void picture(int width, int height, 150 void(*draw)(SkCanvas*), 151 const char* path) { 152 SkPictureRecorder recorder; 153 SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width), 154 SkIntToScalar(height)); 155 draw(recordingCanvas); 156 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture()); 157 SkFILEWStream skpStream(path); 158 // Open SKP files with `SampleApp --picture SKP_FILE` 159 picture->serialize(&skpStream); 160 } 161 162<span id="nullcanvas"></span> 163NullCanvas 164---------- 165 166The null canvas is a canvas that ignores all drawing commands and does 167nothing. 168 169<!--?prettify lang=cc?--> 170 171 #include "SkNullCanvas.h" 172 void picture(int, int, void(*draw)(SkCanvas*), const char*) { 173 sk_sp<SkCanvas> nullCanvas(SkCreateNullCanvas()); 174 draw(nullCanvas); // NoOp 175 } 176 177<span id="skxps"></span> 178SkXPS 179----- 180 181The (*still experimental*) SkXPS canvas writes into an XPS document. 182 183<!--?prettify lang=cc?--> 184 185 #include "SkDocument.h" 186 #include "SkStream.h" 187 void skxps(int width, int height, 188 void(*draw)(SkCanvas*), 189 const char* path) { 190 SkFILEWStream xpsStream(path); 191 sk_sp<SkDocument> xpsDoc(SkDocument::MakeXPS(&pdfStream)); 192 SkCanvas* xpsCanvas = xpsDoc->beginPage(SkIntToScalar(width), 193 SkIntToScalar(height)); 194 draw(xpsCanvas); 195 xpsDoc->close(); 196 } 197 198<span id="sksvg"></span> 199SkSVG 200----- 201 202The (*still experimental*) SkSVG canvas writes into an SVG document. 203 204<!--?prettify lang=cc?--> 205 206 #include "SkStream.h" 207 #include "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 sk_sp<SkCanvas> svgCanvas(SkSVGCanvas::Create( 216 SkRect::MakeWH(SkIntToScalar(src.size().width()), 217 SkIntToScalar(src.size().height())), 218 xmlWriter)); 219 draw(svgCanvas); 220 } 221 222<span id="example"></span> 223Example 224------- 225 226To try this code out, make a [new unit test using instructions 227here](/dev/testing/tests) and wrap these funtions together: 228 229<!--?prettify lang=cc?--> 230 231 #include "SkCanvas.h" 232 #include "SkPath.h" 233 #include "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 ganesh( 256, 256, example, "out_ganesh.png" ); 257 skpdf( 256, 256, example, "out_skpdf.pdf" ); 258 picture(256, 256, example, "out_picture.skp"); 259 } 260