1 SkCanvas Creation 2 ========================= 3 4 First, read about [the SkCanvas API](skcanvas_overview). 5 6 Skia has multiple backends which receive SkCanvas drawing commands, 7 including: 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 17 Each backend has a unique way of creating a SkCanvas. This page gives 18 an example for each: 19 20 <span id="raster"></span> 21 Raster 22 ------ 23 24 The raster backend draws to a block of memory. This memory can be 25 managed by Skia or by the client. 26 27 The recommended way of creating a canvas for the Raster and Ganesh 28 backends is to use a `SkSurface`, which is an object that manages 29 the 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 52 Alternatively, we could have specified the memory for the surface 53 explicitly, 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> 74 GPU 75 ------ 76 77 GPU Surfaces must have a `GrContext` object which manages the 78 GPU context, and related caches for textures and fonts. GrContexts 79 are matched one to one with OpenGL contexts or Vulkan devices. That is, all 80 SkSurfaces that will be rendered to using the same OpenGL context or Vulkan 81 device should share a GrContext. Skia does not create a OpenGL context or Vulkan 82 device for you. In OpenGL mode it also assumes that the correct OpenGL context 83 has 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> 120 SkPDF 121 ----- 122 123 The SkPDF backend uses `SkDocument` instead of `SkSurface`, since 124 a 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> 142 SkPicture 143 --------- 144 145 The 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> 166 NullCanvas 167 ---------- 168 169 The null canvas is a canvas that ignores all drawing commands and does 170 nothing. 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> 181 SkXPS 182 ----- 183 184 The (*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> 205 SkSVG 206 ----- 207 208 The (*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> 228 Example 229 ------- 230 231 To try this code out, make a [new unit test using instructions 232 here](/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